@@ -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) ;
@@ -1720,6 +1839,53 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17201839 }
17211840 } ;
17221841
1842+ // Direct MPP payment (no hop)
1843+ let send_mpp_direct = |source_idx : usize ,
1844+ dest_idx : usize ,
1845+ dest_scids : & [ u64 ] ,
1846+ amt : u64 ,
1847+ payment_ctr : & mut u64 | {
1848+ let source = & nodes[ source_idx] ;
1849+ let dest = & nodes[ dest_idx] ;
1850+ let ( secret, hash) = get_payment_secret_hash ( dest, payment_ctr) ;
1851+ let mut id = PaymentId ( [ 0 ; 32 ] ) ;
1852+ id. 0 [ 0 ..8 ] . copy_from_slice ( & payment_ctr. to_ne_bytes ( ) ) ;
1853+ let succeeded = send_mpp_payment ( source, dest, dest_scids, amt, secret, hash, id) ;
1854+ if succeeded {
1855+ pending_payments. borrow_mut ( ) [ source_idx] . push ( id) ;
1856+ }
1857+ } ;
1858+
1859+ // MPP payment via hop - splits payment across multiple channels on either or both hops
1860+ let send_mpp_hop = |source_idx : usize ,
1861+ middle_idx : usize ,
1862+ middle_scids : & [ u64 ] ,
1863+ dest_idx : usize ,
1864+ dest_scids : & [ u64 ] ,
1865+ amt : u64 ,
1866+ payment_ctr : & mut u64 | {
1867+ let source = & nodes[ source_idx] ;
1868+ let middle = & nodes[ middle_idx] ;
1869+ let dest = & nodes[ dest_idx] ;
1870+ let ( secret, hash) = get_payment_secret_hash ( dest, payment_ctr) ;
1871+ let mut id = PaymentId ( [ 0 ; 32 ] ) ;
1872+ id. 0 [ 0 ..8 ] . copy_from_slice ( & payment_ctr. to_ne_bytes ( ) ) ;
1873+ let succeeded = send_mpp_hop_payment (
1874+ source,
1875+ middle,
1876+ middle_scids,
1877+ dest,
1878+ dest_scids,
1879+ amt,
1880+ secret,
1881+ hash,
1882+ id,
1883+ ) ;
1884+ if succeeded {
1885+ pending_payments. borrow_mut ( ) [ source_idx] . push ( id) ;
1886+ }
1887+ } ;
1888+
17231889 let v = get_slice ! ( 1 ) [ 0 ] ;
17241890 out. locked_write ( format ! ( "READ A BYTE! HANDLING INPUT {:x}...........\n " , v) . as_bytes ( ) ) ;
17251891 match v {
@@ -1904,6 +2070,16 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
19042070 0x6c => send_hop_noret ( 0 , 1 , chan_a, 2 , chan_b, 1 , & mut p_ctr) ,
19052071 0x6d => send_hop_noret ( 2 , 1 , chan_b, 0 , chan_a, 1 , & mut p_ctr) ,
19062072
2073+ // MPP payments
2074+ // 0x70: direct MPP from 0 to 1 (multi A-B channels)
2075+ 0x70 => send_mpp_direct ( 0 , 1 , & chan_ab_scids, 1_000_000 , & mut p_ctr) ,
2076+ // 0x71: MPP 0->1->2, multi channels on first hop (A-B)
2077+ 0x71 => send_mpp_hop ( 0 , 1 , & chan_ab_scids, 2 , & [ chan_b] , 1_000_000 , & mut p_ctr) ,
2078+ // 0x72: MPP 0->1->2, multi channels on both hops (A-B and B-C)
2079+ 0x72 => send_mpp_hop ( 0 , 1 , & chan_ab_scids, 2 , & chan_bc_scids, 1_000_000 , & mut p_ctr) ,
2080+ // 0x73: MPP 0->1->2, multi channels on second hop (B-C)
2081+ 0x73 => send_mpp_hop ( 0 , 1 , & [ chan_a] , 2 , & chan_bc_scids, 1_000_000 , & mut p_ctr) ,
2082+
19072083 0x80 => {
19082084 let mut max_feerate = last_htlc_clear_fee_a;
19092085 if !anchors {
0 commit comments