55// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66// accordance with one or both of these licenses.
77
8- #![ cfg( any( test, cln_test, lnd_test, vss_test) ) ]
8+ #![ cfg( any( test, cln_test, lnd_test, eclair_test , vss_test) ) ]
99#![ allow( dead_code) ]
1010
1111pub ( crate ) mod external_node;
@@ -56,9 +56,24 @@ use rand::distr::Alphanumeric;
5656use rand:: { rng, Rng } ;
5757use serde_json:: { json, Value } ;
5858
59+ /// Shared timeout (in seconds) for waiting on LDK events and external node operations.
60+ pub ( crate ) const INTEROP_TIMEOUT_SECS : u64 = 60 ;
61+
5962macro_rules! expect_event {
6063 ( $node: expr, $event_type: ident) => { {
61- match $node. next_event_async( ) . await {
64+ let event = tokio:: time:: timeout(
65+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
66+ $node. next_event_async( ) ,
67+ )
68+ . await
69+ . unwrap_or_else( |_| {
70+ panic!(
71+ "{} timed out waiting for {} event after 60s" ,
72+ $node. node_id( ) ,
73+ std:: stringify!( $event_type)
74+ )
75+ } ) ;
76+ match event {
6277 ref e @ Event :: $event_type { .. } => {
6378 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
6479 $node. event_handled( ) . unwrap( ) ;
@@ -74,7 +89,15 @@ pub(crate) use expect_event;
7489
7590macro_rules! expect_channel_pending_event {
7691 ( $node: expr, $counterparty_node_id: expr) => { {
77- match $node. next_event_async( ) . await {
92+ let event = tokio:: time:: timeout(
93+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
94+ $node. next_event_async( ) ,
95+ )
96+ . await
97+ . unwrap_or_else( |_| {
98+ panic!( "{} timed out waiting for ChannelPending event after 60s" , $node. node_id( ) )
99+ } ) ;
100+ match event {
78101 ref e @ Event :: ChannelPending { funding_txo, counterparty_node_id, .. } => {
79102 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
80103 assert_eq!( counterparty_node_id, $counterparty_node_id) ;
@@ -92,7 +115,15 @@ pub(crate) use expect_channel_pending_event;
92115
93116macro_rules! expect_channel_ready_event {
94117 ( $node: expr, $counterparty_node_id: expr) => { {
95- match $node. next_event_async( ) . await {
118+ let event = tokio:: time:: timeout(
119+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
120+ $node. next_event_async( ) ,
121+ )
122+ . await
123+ . unwrap_or_else( |_| {
124+ panic!( "{} timed out waiting for ChannelReady event after 60s" , $node. node_id( ) )
125+ } ) ;
126+ match event {
96127 ref e @ Event :: ChannelReady { user_channel_id, counterparty_node_id, .. } => {
97128 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
98129 assert_eq!( counterparty_node_id, Some ( $counterparty_node_id) ) ;
@@ -112,7 +143,15 @@ macro_rules! expect_channel_ready_events {
112143 ( $node: expr, $counterparty_node_id_a: expr, $counterparty_node_id_b: expr) => { {
113144 let mut ids = Vec :: new( ) ;
114145 for _ in 0 ..2 {
115- match $node. next_event_async( ) . await {
146+ let event = tokio:: time:: timeout(
147+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
148+ $node. next_event_async( ) ,
149+ )
150+ . await
151+ . unwrap_or_else( |_| {
152+ panic!( "{} timed out waiting for ChannelReady event after 60s" , $node. node_id( ) )
153+ } ) ;
154+ match event {
116155 ref e @ Event :: ChannelReady { counterparty_node_id, .. } => {
117156 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
118157 ids. push( counterparty_node_id) ;
@@ -138,7 +177,15 @@ pub(crate) use expect_channel_ready_events;
138177
139178macro_rules! expect_splice_pending_event {
140179 ( $node: expr, $counterparty_node_id: expr) => { {
141- match $node. next_event_async( ) . await {
180+ let event = tokio:: time:: timeout(
181+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
182+ $node. next_event_async( ) ,
183+ )
184+ . await
185+ . unwrap_or_else( |_| {
186+ panic!( "{} timed out waiting for SplicePending event after 60s" , $node. node_id( ) )
187+ } ) ;
188+ match event {
142189 ref e @ Event :: SplicePending { new_funding_txo, counterparty_node_id, .. } => {
143190 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
144191 assert_eq!( counterparty_node_id, $counterparty_node_id) ;
@@ -156,19 +203,27 @@ pub(crate) use expect_splice_pending_event;
156203
157204macro_rules! expect_payment_received_event {
158205 ( $node: expr, $amount_msat: expr) => { {
159- match $node. next_event_async( ) . await {
206+ let event = tokio:: time:: timeout(
207+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
208+ $node. next_event_async( ) ,
209+ )
210+ . await
211+ . unwrap_or_else( |_| {
212+ panic!( "{} timed out waiting for PaymentReceived event after 60s" , $node. node_id( ) )
213+ } ) ;
214+ match event {
160215 ref e @ Event :: PaymentReceived { payment_id, amount_msat, .. } => {
161216 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
162217 assert_eq!( amount_msat, $amount_msat) ;
163218 let payment = $node. payment( & payment_id. unwrap( ) ) . unwrap( ) ;
164- if !matches!( payment. kind, PaymentKind :: Onchain { .. } ) {
219+ if !matches!( payment. kind, ldk_node :: payment :: PaymentKind :: Onchain { .. } ) {
165220 assert_eq!( payment. fee_paid_msat, None ) ;
166221 }
167222 $node. event_handled( ) . unwrap( ) ;
168223 payment_id
169224 } ,
170225 ref e => {
171- panic!( "{} got unexpected event!: {:?}" , std:: stringify!( node_b ) , e) ;
226+ panic!( "{} got unexpected event!: {:?}" , std:: stringify!( $node ) , e) ;
172227 } ,
173228 }
174229 } } ;
@@ -178,7 +233,18 @@ pub(crate) use expect_payment_received_event;
178233
179234macro_rules! expect_payment_claimable_event {
180235 ( $node: expr, $payment_id: expr, $payment_hash: expr, $claimable_amount_msat: expr) => { {
181- match $node. next_event_async( ) . await {
236+ let event = tokio:: time:: timeout(
237+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
238+ $node. next_event_async( ) ,
239+ )
240+ . await
241+ . unwrap_or_else( |_| {
242+ panic!(
243+ "{} timed out waiting for PaymentClaimable event after 60s" ,
244+ std:: stringify!( $node)
245+ )
246+ } ) ;
247+ match event {
182248 ref e @ Event :: PaymentClaimable {
183249 payment_id,
184250 payment_hash,
@@ -203,7 +269,15 @@ pub(crate) use expect_payment_claimable_event;
203269
204270macro_rules! expect_payment_successful_event {
205271 ( $node: expr, $payment_id: expr, $fee_paid_msat: expr) => { {
206- match $node. next_event_async( ) . await {
272+ let event = tokio:: time:: timeout(
273+ std:: time:: Duration :: from_secs( crate :: common:: INTEROP_TIMEOUT_SECS ) ,
274+ $node. next_event_async( ) ,
275+ )
276+ . await
277+ . unwrap_or_else( |_| {
278+ panic!( "{} timed out waiting for PaymentSuccessful event after 60s" , $node. node_id( ) )
279+ } ) ;
280+ match event {
207281 ref e @ Event :: PaymentSuccessful { payment_id, fee_paid_msat, .. } => {
208282 println!( "{} got event {:?}" , $node. node_id( ) , e) ;
209283 if let Some ( fee_msat) = $fee_paid_msat {
@@ -388,6 +462,9 @@ macro_rules! setup_builder {
388462
389463pub ( crate ) use setup_builder;
390464
465+ #[ cfg( any( cln_test, lnd_test, eclair_test) ) ]
466+ pub ( crate ) mod scenarios;
467+
391468pub ( crate ) fn setup_two_nodes (
392469 chain_source : & TestChainSource , allow_0conf : bool , anchor_channels : bool ,
393470 anchors_trusted_no_reserve : bool ,
@@ -1697,3 +1774,72 @@ impl TestSyncStoreInner {
16971774 self . do_list ( primary_namespace, secondary_namespace)
16981775 }
16991776}
1777+
1778+ /// Generates 16 individual `#[tokio::test]` functions covering every combination
1779+ /// of (Phase, disconnect Side, CloseType, close Side):
1780+ /// 2 phases × 2 disconnect sides × 2 close types × 2 close initiators = 16.
1781+ ///
1782+ /// PayType is fixed to Bolt11 — keysend vs bolt11 doesn't affect channel close
1783+ /// behavior and is already covered by dedicated named tests (`test_*_receive_keysend`,
1784+ /// `test_*_receive_payments`). Do NOT add a Keysend axis here: CLN and Eclair
1785+ /// have known keysend interop issues (see `#[ignore]` tests in their entry points).
1786+ ///
1787+ /// Usage (inside each `integration_tests_*.rs`):
1788+ /// ```ignore
1789+ /// interop_combo_tests!(test_lnd, setup_clients, setup_ldk_node);
1790+ /// ```
1791+ #[ macro_export]
1792+ macro_rules! interop_combo_tests {
1793+ ( $prefix: ident, $setup_clients: ident, $setup_ldk_node: ident) => {
1794+ $crate:: interop_combo_tests!(
1795+ @phase $prefix, $setup_clients, $setup_ldk_node,
1796+ [ payment, Phase :: Payment ] , [ idle, Phase :: Idle ]
1797+ ) ;
1798+ } ;
1799+
1800+ ( @phase $prefix: ident, $sc: ident, $sn: ident, $( [ $pn: ident, $pv: expr] ) ,+) => {
1801+ $(
1802+ $crate:: interop_combo_tests!(
1803+ @disc $prefix, $sc, $sn, $pn, $pv,
1804+ [ ldk, Side :: Ldk ] , [ ext, Side :: External ]
1805+ ) ;
1806+ ) +
1807+ } ;
1808+
1809+ ( @disc $prefix: ident, $sc: ident, $sn: ident, $pn: ident, $pv: expr, $( [ $dn: ident, $dv: expr] ) ,+) => {
1810+ $(
1811+ $crate:: interop_combo_tests!(
1812+ @close $prefix, $sc, $sn, $pn, $pv, $dn, $dv,
1813+ [ coop, CloseType :: Cooperative ] , [ force, CloseType :: Force ]
1814+ ) ;
1815+ ) +
1816+ } ;
1817+
1818+ ( @close $prefix: ident, $sc: ident, $sn: ident, $pn: ident, $pv: expr, $dn: ident, $dv: expr,
1819+ $( [ $cn: ident, $cv: expr] ) ,+) => {
1820+ $(
1821+ $crate:: interop_combo_tests!(
1822+ @ci $prefix, $sc, $sn, $pn, $pv, $dn, $dv, $cn, $cv,
1823+ [ ldk, Side :: Ldk ] , [ ext, Side :: External ]
1824+ ) ;
1825+ ) +
1826+ } ;
1827+
1828+ ( @ci $prefix: ident, $sc: ident, $sn: ident, $pn: ident, $pv: expr, $dn: ident, $dv: expr,
1829+ $cn: ident, $cv: expr, $( [ $cin: ident, $civ: expr] ) ,+) => {
1830+ $(
1831+ paste:: paste! {
1832+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
1833+ async fn [ <$prefix _combo_ $pn _ $dn _ $cn _ $cin>] ( ) {
1834+ let ( bitcoind, electrs, peer) = $sc( ) . await ;
1835+ let node = $sn( ) ;
1836+ run_interop_combo_test(
1837+ & node, & peer, & bitcoind, & electrs,
1838+ $pv, $dv, $cv, $civ, PayType :: Bolt11 ,
1839+ ) . await ;
1840+ node. stop( ) . unwrap( ) ;
1841+ }
1842+ }
1843+ ) +
1844+ } ;
1845+ }
0 commit comments