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