@@ -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,94 @@ 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+ async fn settle_force_close_balance < E : ElectrumApi > (
440+ node : & TestNode , counterparty_node_id : PublicKey , peer_node : & TestNode ,
441+ bitcoind : & BitcoindClient , electrsd : & E ,
442+ ) {
443+ let balances = node. list_balances ( ) ;
444+ if balances. lightning_balances . len ( ) == 1 {
445+ match balances. lightning_balances [ 0 ] {
446+ LightningBalance :: ClaimableAwaitingConfirmations {
447+ counterparty_node_id : actual_counterparty_node_id,
448+ confirmation_height,
449+ ..
450+ } => {
451+ assert_eq ! ( actual_counterparty_node_id, counterparty_node_id) ;
452+ let cur_height = node. status ( ) . current_best_block . height ;
453+ let blocks_to_go = confirmation_height - cur_height;
454+ generate_blocks_and_wait ( bitcoind, electrsd, blocks_to_go as usize ) . await ;
455+ node. sync_wallets ( ) . unwrap ( ) ;
456+ peer_node. sync_wallets ( ) . unwrap ( ) ;
457+ } ,
458+ _ => panic ! ( "Unexpected balance state!" ) ,
459+ }
460+ } else {
461+ assert ! ( balances. lightning_balances. is_empty( ) , "Unexpected balance state: {:?}" , balances) ;
462+ assert_eq ! ( balances. pending_balances_from_channel_closures. len( ) , 1 ) ;
463+ }
464+
465+ for _ in 0 ..6 {
466+ if node. list_balances ( ) . lightning_balances . is_empty ( ) {
467+ break ;
468+ }
469+ generate_blocks_and_wait ( bitcoind, electrsd, 1 ) . await ;
470+ node. sync_wallets ( ) . unwrap ( ) ;
471+ peer_node. sync_wallets ( ) . unwrap ( ) ;
472+ }
473+
474+ let balances = node. list_balances ( ) ;
475+ assert ! ( balances. lightning_balances. is_empty( ) , "Unexpected balance state: {:?}" , balances) ;
476+ assert_eq ! ( balances. pending_balances_from_channel_closures. len( ) , 1 ) ;
477+ match balances. pending_balances_from_channel_closures [ 0 ] {
478+ PendingSweepBalance :: BroadcastAwaitingConfirmation { .. } => {
479+ generate_blocks_and_wait ( bitcoind, electrsd, 1 ) . await ;
480+ node. sync_wallets ( ) . unwrap ( ) ;
481+ peer_node. sync_wallets ( ) . unwrap ( ) ;
482+
483+ assert ! ( node. list_balances( ) . lightning_balances. is_empty( ) ) ;
484+ assert_eq ! ( node. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
485+ match node. list_balances ( ) . pending_balances_from_channel_closures [ 0 ] {
486+ PendingSweepBalance :: AwaitingThresholdConfirmations { .. } => { } ,
487+ _ => panic ! ( "Unexpected balance state!" ) ,
488+ }
489+ } ,
490+ PendingSweepBalance :: AwaitingThresholdConfirmations { .. } => { } ,
491+ _ => panic ! ( "Unexpected balance state!" ) ,
492+ }
493+
494+ generate_blocks_and_wait ( bitcoind, electrsd, 5 ) . await ;
495+ node. sync_wallets ( ) . unwrap ( ) ;
496+ peer_node. sync_wallets ( ) . unwrap ( ) ;
497+ }
498+
410499#[ derive( Clone ) ]
411500pub ( crate ) enum TestChainSource < ' a > {
412501 Esplora ( & ' a ElectrsD ) ,
@@ -1474,84 +1563,11 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
14741563 node_b. sync_wallets ( ) . unwrap ( ) ;
14751564
14761565 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-
1566+ settle_force_close_balance ( & node_b, node_a. node_id ( ) , & node_a, & bitcoind, electrsd) . await ;
14951567 assert ! ( node_b. list_balances( ) . lightning_balances. is_empty( ) ) ;
14961568 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 ( ) ;
15041569
1505- assert ! ( node_b. list_balances( ) . lightning_balances. is_empty( ) ) ;
1506- 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 ( ) ;
1545-
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 ( ) ;
1570+ settle_force_close_balance ( & node_a, node_b. node_id ( ) , & node_b, & bitcoind, electrsd) . await ;
15551571 } else {
15561572 assert_eq ! ( node_a. list_balances( ) . lightning_balances. len( ) , 1 ) ;
15571573 assert ! ( node_a. list_balances( ) . pending_balances_from_channel_closures. is_empty( ) ) ;
@@ -1597,6 +1613,25 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
15971613 assert ! ( node_b. list_balances( ) . pending_balances_from_channel_closures. is_empty( ) ) ;
15981614 }
15991615
1616+ if force_close {
1617+ assert_any_node_has_onchain_tx_type (
1618+ & [ ( "node_a" , & node_a) , ( "node_b" , & node_b) ] ,
1619+ "UnilateralClose" ,
1620+ |tx_type| matches ! ( tx_type, TransactionType :: UnilateralClose { .. } ) ,
1621+ ) ;
1622+ assert_any_node_has_onchain_tx_type (
1623+ & [ ( "node_a" , & node_a) , ( "node_b" , & node_b) ] ,
1624+ "Sweep" ,
1625+ |tx_type| matches ! ( tx_type, TransactionType :: Sweep { .. } ) ,
1626+ ) ;
1627+ } else {
1628+ assert_any_node_has_onchain_tx_type (
1629+ & [ ( "node_a" , & node_a) , ( "node_b" , & node_b) ] ,
1630+ "CooperativeClose" ,
1631+ |tx_type| matches ! ( tx_type, TransactionType :: CooperativeClose { .. } ) ,
1632+ ) ;
1633+ }
1634+
16001635 let sum_of_all_payments_sat = ( push_msat
16011636 + invoice_amount_1_msat
16021637 + overpaid_amount_msat
@@ -1619,20 +1654,20 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
16191654 assert_eq ! ( node_b. list_balances( ) . total_anchor_channels_reserve_sats, 0 ) ;
16201655
16211656 // 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- ) ;
1657+ let node_a_inbound_onchain_count = node_a
1658+ . list_payments_with_filter ( |p| {
1659+ p. direction == PaymentDirection :: Inbound
1660+ && matches ! ( p. kind, PaymentKind :: Onchain { .. } )
1661+ } )
1662+ . len ( ) ;
1663+ let node_b_inbound_onchain_count = node_b
1664+ . list_payments_with_filter ( |p| {
1665+ p . direction == PaymentDirection :: Inbound
1666+ && matches ! ( p . kind , PaymentKind :: Onchain { .. } )
1667+ } )
1668+ . len ( ) ;
1669+ assert ! ( node_a_inbound_onchain_count >= 3 ) ;
1670+ assert ! ( node_b_inbound_onchain_count >= 2 ) ;
16361671
16371672 // Check we handled all events
16381673 assert_eq ! ( node_a. next_event( ) , None ) ;
0 commit comments