@@ -656,6 +656,125 @@ fn send_hop_payment(
656656 }
657657}
658658
659+ /// Send an MPP payment directly from source to dest using multiple channels.
660+ #[ inline]
661+ fn send_mpp_payment (
662+ source : & ChanMan , dest : & ChanMan , dest_scids : & [ u64 ] , amt : u64 , payment_secret : PaymentSecret ,
663+ payment_hash : PaymentHash , payment_id : PaymentId ,
664+ ) -> bool {
665+ let num_paths = dest_scids. len ( ) ;
666+ if num_paths == 0 {
667+ return false ;
668+ }
669+
670+ let amt_per_path = amt / num_paths as u64 ;
671+ let mut paths = Vec :: with_capacity ( num_paths) ;
672+
673+ for ( i, & dest_scid) in dest_scids. iter ( ) . enumerate ( ) {
674+ let path_amt = if i == num_paths - 1 {
675+ amt - amt_per_path * ( num_paths as u64 - 1 )
676+ } else {
677+ amt_per_path
678+ } ;
679+
680+ paths. push ( Path {
681+ hops : vec ! [ RouteHop {
682+ pubkey: dest. get_our_node_id( ) ,
683+ node_features: dest. node_features( ) ,
684+ short_channel_id: dest_scid,
685+ channel_features: dest. channel_features( ) ,
686+ fee_msat: path_amt,
687+ cltv_expiry_delta: 200 ,
688+ maybe_announced_channel: true ,
689+ } ] ,
690+ blinded_tail : None ,
691+ } ) ;
692+ }
693+
694+ let route_params = RouteParameters :: from_payment_params_and_value (
695+ PaymentParameters :: from_node_id ( dest. get_our_node_id ( ) , TEST_FINAL_CLTV ) ,
696+ amt,
697+ ) ;
698+ let route = Route { paths, route_params : Some ( route_params) } ;
699+ let onion = RecipientOnionFields :: secret_only ( payment_secret) ;
700+ let res = source. send_payment_with_route ( route, payment_hash, onion, payment_id) ;
701+ match res {
702+ Err ( _) => false ,
703+ Ok ( ( ) ) => check_payment_send_events ( source, payment_id) ,
704+ }
705+ }
706+
707+ /// Send an MPP payment from source to dest via middle node.
708+ /// Supports multiple channels on either or both hops.
709+ #[ inline]
710+ fn send_mpp_hop_payment (
711+ source : & ChanMan , middle : & ChanMan , middle_scids : & [ u64 ] , dest : & ChanMan , dest_scids : & [ u64 ] ,
712+ amt : u64 , payment_secret : PaymentSecret , payment_hash : PaymentHash , payment_id : PaymentId ,
713+ ) -> bool {
714+ // Create paths by pairing middle_scids with dest_scids
715+ let num_paths = middle_scids. len ( ) . max ( dest_scids. len ( ) ) ;
716+ if num_paths == 0 {
717+ return false ;
718+ }
719+
720+ let first_hop_fee = 50_000 ;
721+ let amt_per_path = amt / num_paths as u64 ;
722+ let fee_per_path = first_hop_fee / num_paths as u64 ;
723+ let mut paths = Vec :: with_capacity ( num_paths) ;
724+
725+ for i in 0 ..num_paths {
726+ let middle_scid = middle_scids[ i % middle_scids. len ( ) ] ;
727+ let dest_scid = dest_scids[ i % dest_scids. len ( ) ] ;
728+
729+ let path_amt = if i == num_paths - 1 {
730+ amt - amt_per_path * ( num_paths as u64 - 1 )
731+ } else {
732+ amt_per_path
733+ } ;
734+ let path_fee = if i == num_paths - 1 {
735+ first_hop_fee - fee_per_path * ( num_paths as u64 - 1 )
736+ } else {
737+ fee_per_path
738+ } ;
739+
740+ paths. push ( Path {
741+ hops : vec ! [
742+ RouteHop {
743+ pubkey: middle. get_our_node_id( ) ,
744+ node_features: middle. node_features( ) ,
745+ short_channel_id: middle_scid,
746+ channel_features: middle. channel_features( ) ,
747+ fee_msat: path_fee,
748+ cltv_expiry_delta: 100 ,
749+ maybe_announced_channel: true ,
750+ } ,
751+ RouteHop {
752+ pubkey: dest. get_our_node_id( ) ,
753+ node_features: dest. node_features( ) ,
754+ short_channel_id: dest_scid,
755+ channel_features: dest. channel_features( ) ,
756+ fee_msat: path_amt,
757+ cltv_expiry_delta: 200 ,
758+ maybe_announced_channel: true ,
759+ } ,
760+ ] ,
761+ blinded_tail : None ,
762+ } ) ;
763+ }
764+
765+ let route_params = RouteParameters :: from_payment_params_and_value (
766+ PaymentParameters :: from_node_id ( dest. get_our_node_id ( ) , TEST_FINAL_CLTV ) ,
767+ amt,
768+ ) ;
769+ let route = Route { paths, route_params : Some ( route_params) } ;
770+ let onion = RecipientOnionFields :: secret_only ( payment_secret) ;
771+ let res = source. send_payment_with_route ( route, payment_hash, onion, payment_id) ;
772+ match res {
773+ Err ( _) => false ,
774+ Ok ( ( ) ) => check_payment_send_events ( source, payment_id) ,
775+ }
776+ }
777+
659778#[ inline]
660779pub fn do_test < Out : Output > ( data : & [ u8 ] , underlying_out : Out , anchors : bool ) {
661780 let out = SearchingOutput :: new ( underlying_out) ;
@@ -1724,6 +1843,53 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17241843 }
17251844 } ;
17261845
1846+ // Direct MPP payment (no hop)
1847+ let send_mpp_direct = |source_idx : usize ,
1848+ dest_idx : usize ,
1849+ dest_scids : & [ u64 ] ,
1850+ amt : u64 ,
1851+ payment_ctr : & mut u64 | {
1852+ let source = & nodes[ source_idx] ;
1853+ let dest = & nodes[ dest_idx] ;
1854+ let ( secret, hash) = get_payment_secret_hash ( dest, payment_ctr) ;
1855+ let mut id = PaymentId ( [ 0 ; 32 ] ) ;
1856+ id. 0 [ 0 ..8 ] . copy_from_slice ( & payment_ctr. to_ne_bytes ( ) ) ;
1857+ let succeeded = send_mpp_payment ( source, dest, dest_scids, amt, secret, hash, id) ;
1858+ if succeeded {
1859+ pending_payments. borrow_mut ( ) [ source_idx] . push ( id) ;
1860+ }
1861+ } ;
1862+
1863+ // MPP payment via hop - splits payment across multiple channels on either or both hops
1864+ let send_mpp_hop = |source_idx : usize ,
1865+ middle_idx : usize ,
1866+ middle_scids : & [ u64 ] ,
1867+ dest_idx : usize ,
1868+ dest_scids : & [ u64 ] ,
1869+ amt : u64 ,
1870+ payment_ctr : & mut u64 | {
1871+ let source = & nodes[ source_idx] ;
1872+ let middle = & nodes[ middle_idx] ;
1873+ let dest = & nodes[ dest_idx] ;
1874+ let ( secret, hash) = get_payment_secret_hash ( dest, payment_ctr) ;
1875+ let mut id = PaymentId ( [ 0 ; 32 ] ) ;
1876+ id. 0 [ 0 ..8 ] . copy_from_slice ( & payment_ctr. to_ne_bytes ( ) ) ;
1877+ let succeeded = send_mpp_hop_payment (
1878+ source,
1879+ middle,
1880+ middle_scids,
1881+ dest,
1882+ dest_scids,
1883+ amt,
1884+ secret,
1885+ hash,
1886+ id,
1887+ ) ;
1888+ if succeeded {
1889+ pending_payments. borrow_mut ( ) [ source_idx] . push ( id) ;
1890+ }
1891+ } ;
1892+
17271893 let v = get_slice ! ( 1 ) [ 0 ] ;
17281894 out. locked_write ( format ! ( "READ A BYTE! HANDLING INPUT {:x}...........\n " , v) . as_bytes ( ) ) ;
17291895 match v {
@@ -1908,6 +2074,16 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
19082074 0x6c => send_hop_noret ( 0 , 1 , chan_a, 2 , chan_b, 1 , & mut p_ctr) ,
19092075 0x6d => send_hop_noret ( 2 , 1 , chan_b, 0 , chan_a, 1 , & mut p_ctr) ,
19102076
2077+ // MPP payments
2078+ // 0x70: direct MPP from 0 to 1 (multi A-B channels)
2079+ 0x70 => send_mpp_direct ( 0 , 1 , & chan_ab_scids, 1_000_000 , & mut p_ctr) ,
2080+ // 0x71: MPP 0->1->2, multi channels on first hop (A-B)
2081+ 0x71 => send_mpp_hop ( 0 , 1 , & chan_ab_scids, 2 , & [ chan_b] , 1_000_000 , & mut p_ctr) ,
2082+ // 0x72: MPP 0->1->2, multi channels on both hops (A-B and B-C)
2083+ 0x72 => send_mpp_hop ( 0 , 1 , & chan_ab_scids, 2 , & chan_bc_scids, 1_000_000 , & mut p_ctr) ,
2084+ // 0x73: MPP 0->1->2, multi channels on second hop (B-C)
2085+ 0x73 => send_mpp_hop ( 0 , 1 , & [ chan_a] , 2 , & chan_bc_scids, 1_000_000 , & mut p_ctr) ,
2086+
19112087 0x80 => {
19122088 let mut max_feerate = last_htlc_clear_fee_a;
19132089 if !anchors {
0 commit comments