@@ -43,6 +43,7 @@ use ldk_node::config::{
4343use ldk_node:: entropy:: { generate_entropy_mnemonic, NodeEntropy } ;
4444use ldk_node:: io:: sqlite_store:: SqliteStore ;
4545use ldk_node:: payment:: { PaymentDirection , PaymentKind , PaymentStatus } ;
46+ use ldk_node:: probing:: ProbingConfig ;
4647use ldk_node:: {
4748 Builder , ChannelShutdownState , CustomTlvRecord , Event , LightningBalance , Node , NodeError ,
4849 PendingSweepBalance , UserChannelId ,
@@ -403,9 +404,9 @@ pub(crate) fn random_config(anchor_channels: bool) -> TestConfig {
403404}
404405
405406#[ cfg( feature = "uniffi" ) ]
406- type TestNode = Arc < Node > ;
407+ pub ( crate ) type TestNode = Arc < Node > ;
407408#[ cfg( not( feature = "uniffi" ) ) ]
408- type TestNode = Node ;
409+ pub ( crate ) type TestNode = Node ;
409410
410411#[ derive( Clone ) ]
411412pub ( crate ) enum TestChainSource < ' a > {
@@ -437,6 +438,7 @@ pub(crate) struct TestConfig {
437438 pub async_payments_role : Option < AsyncPaymentsRole > ,
438439 pub wallet_rescan_from_height : Option < u32 > ,
439440 pub force_wallet_full_scan : bool ,
441+ pub probing : Option < ProbingConfig > ,
440442}
441443
442444impl Default for TestConfig {
@@ -458,6 +460,7 @@ impl Default for TestConfig {
458460 async_payments_role,
459461 wallet_rescan_from_height,
460462 force_wallet_full_scan,
463+ probing : None ,
461464 }
462465 }
463466}
@@ -598,6 +601,10 @@ pub(crate) fn setup_node(chain_source: &TestChainSource, config: TestConfig) ->
598601
599602 builder. set_async_payments_role ( config. async_payments_role ) . unwrap ( ) ;
600603
604+ if let Some ( probing) = config. probing {
605+ builder. set_probing_config ( probing. into ( ) ) ;
606+ }
607+
601608 let node = match config. store_type {
602609 TestStoreType :: TestSyncStore => {
603610 let kv_store = TestSyncStore :: new ( config. node_config . storage_dir_path . into ( ) ) ;
@@ -699,6 +706,37 @@ pub(crate) async fn wait_for_outpoint_spend<E: ElectrumApi>(electrs: &E, outpoin
699706 . await ;
700707}
701708
709+ /// Polls the channel from `source_node` to `counterparty_node` until it reports `is_usable`
710+ /// and can carry an HTLC of `min_amount_msat` from `source_node`'s side.
711+ ///
712+ /// After `ChannelReady`, channel-monitor persistence can lag for tens of seconds on slow
713+ /// CI runners; during that window `send_probe`/`send_payment` reject with
714+ /// `ParameterError("...monitor update is in progress...")`. This helper gives tests a
715+ /// deterministic readiness gate instead of racing the monitor-update pipeline.
716+ pub ( crate ) async fn wait_for_channel_ready_to_send (
717+ source_node : & TestNode , counterparty_node : & TestNode , min_amount_msat : u64 ,
718+ ) {
719+ let counterparty = counterparty_node. node_id ( ) ;
720+ let deadline = tokio:: time:: Instant :: now ( ) + Duration :: from_secs ( 180 ) ;
721+ while tokio:: time:: Instant :: now ( ) < deadline {
722+ let ready = source_node. list_channels ( ) . iter ( ) . any ( |c| {
723+ c. counterparty_node_id == counterparty
724+ && c. is_usable
725+ && c. next_outbound_htlc_limit_msat >= min_amount_msat
726+ } ) ;
727+ if ready {
728+ return ;
729+ }
730+ tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
731+ }
732+ panic ! (
733+ "channel from {} to {} not ready to send {} msat within 180s" ,
734+ source_node. node_id( ) ,
735+ counterparty,
736+ min_amount_msat,
737+ ) ;
738+ }
739+
702740pub ( crate ) async fn exponential_backoff_poll < T , F > ( mut poll : F ) -> T
703741where
704742 F : FnMut ( ) -> Option < T > ,
@@ -824,12 +862,18 @@ pub async fn open_channel(
824862 node_a : & TestNode , node_b : & TestNode , funding_amount_sat : u64 , should_announce : bool ,
825863 electrsd : & ElectrsD ,
826864) -> OutPoint {
827- open_channel_push_amt ( node_a, node_b, funding_amount_sat, None , should_announce, electrsd) . await
865+ let funding_txo =
866+ open_channel_no_wait ( node_a, node_b, funding_amount_sat, None , should_announce) . await ;
867+ wait_for_tx ( & electrsd. client , funding_txo. txid ) . await ;
868+ funding_txo
828869}
829870
830- pub async fn open_channel_push_amt (
871+ /// Like [`open_channel`] but skips the `wait_for_tx` electrum check so that
872+ /// multiple channels can be opened back-to-back before any blocks are mined.
873+ /// The caller is responsible for mining blocks and confirming the funding txs.
874+ pub async fn open_channel_no_wait (
831875 node_a : & TestNode , node_b : & TestNode , funding_amount_sat : u64 , push_amount_msat : Option < u64 > ,
832- should_announce : bool , electrsd : & ElectrsD ,
876+ should_announce : bool ,
833877) -> OutPoint {
834878 if should_announce {
835879 node_a
@@ -857,11 +901,20 @@ pub async fn open_channel_push_amt(
857901 let funding_txo_a = expect_channel_pending_event ! ( node_a, node_b. node_id( ) ) ;
858902 let funding_txo_b = expect_channel_pending_event ! ( node_b, node_a. node_id( ) ) ;
859903 assert_eq ! ( funding_txo_a, funding_txo_b) ;
860- wait_for_tx ( & electrsd. client , funding_txo_a. txid ) . await ;
861-
862904 funding_txo_a
863905}
864906
907+ pub async fn open_channel_push_amt (
908+ node_a : & TestNode , node_b : & TestNode , funding_amount_sat : u64 , push_amount_msat : Option < u64 > ,
909+ should_announce : bool , electrsd : & ElectrsD ,
910+ ) -> OutPoint {
911+ let funding_txo =
912+ open_channel_no_wait ( node_a, node_b, funding_amount_sat, push_amount_msat, should_announce)
913+ . await ;
914+ wait_for_tx ( & electrsd. client , funding_txo. txid ) . await ;
915+ funding_txo
916+ }
917+
865918pub async fn open_channel_with_all (
866919 node_a : & TestNode , node_b : & TestNode , should_announce : bool , electrsd : & ElectrsD ,
867920) -> OutPoint {
0 commit comments