@@ -1746,9 +1746,9 @@ pub struct FinalOnionHopData {
17461746
17471747mod fuzzy_internal_msgs {
17481748 use bitcoin:: secp256k1:: PublicKey ;
1749- use crate :: blinded_path:: payment:: { PaymentConstraints , PaymentContext , PaymentRelay } ;
1749+ use crate :: blinded_path:: payment:: { BlindedPaymentPath , PaymentConstraints , PaymentContext , PaymentRelay } ;
17501750 use crate :: types:: payment:: { PaymentPreimage , PaymentSecret } ;
1751- use crate :: types:: features:: BlindedHopFeatures ;
1751+ use crate :: types:: features:: { BlindedHopFeatures , Bolt12InvoiceFeatures } ;
17521752 use super :: { FinalOnionHopData , TrampolineOnionPacket } ;
17531753
17541754 #[ allow( unused_imports) ]
@@ -1830,14 +1830,41 @@ mod fuzzy_internal_msgs {
18301830 }
18311831 }
18321832
1833- pub ( crate ) enum OutboundTrampolinePayload {
1833+ pub ( crate ) enum OutboundTrampolinePayload < ' a > {
18341834 #[ allow( unused) ]
18351835 Forward {
18361836 /// The value, in msat, of the payment after this hop's fee is deducted.
18371837 amt_to_forward : u64 ,
18381838 outgoing_cltv_value : u32 ,
1839- /// The node id to which the trampoline node must find a route
1839+ /// The node id to which the trampoline node must find a route.
18401840 outgoing_node_id : PublicKey ,
1841+ } ,
1842+ #[ allow( unused) ]
1843+ /// This is the last Trampoline hop, whereupon the Trampoline forward mechanism is exited,
1844+ /// and payment data is relayed using non-Trampoline blinded hops
1845+ LegacyBlindedPathEntry {
1846+ /// The value, in msat, of the payment after this hop's fee is deducted.
1847+ amt_to_forward : u64 ,
1848+ outgoing_cltv_value : u32 ,
1849+ /// List of blinded path options the last trampoline hop may choose to route through.
1850+ payment_paths : Vec < BlindedPaymentPath > ,
1851+ /// If applicable, features of the BOLT12 invoice being paid.
1852+ invoice_features : Option < Bolt12InvoiceFeatures > ,
1853+ } ,
1854+ #[ allow( unused) ]
1855+ BlindedForward {
1856+ encrypted_tlvs : & ' a Vec < u8 > ,
1857+ intro_node_blinding_point : Option < PublicKey > ,
1858+ } ,
1859+ #[ allow( unused) ]
1860+ BlindedReceive {
1861+ sender_intended_htlc_amt_msat : u64 ,
1862+ total_msat : u64 ,
1863+ cltv_expiry_height : u32 ,
1864+ encrypted_tlvs : & ' a Vec < u8 > ,
1865+ intro_node_blinding_point : Option < PublicKey > , // Set if the introduction node of the blinded path is the final node
1866+ keysend_preimage : Option < PaymentPreimage > ,
1867+ custom_tlvs : & ' a Vec < ( u64 , Vec < u8 > ) > ,
18411868 }
18421869 }
18431870
@@ -2754,7 +2781,7 @@ impl<'a> Writeable for OutboundOnionPayload<'a> {
27542781 }
27552782}
27562783
2757- impl Writeable for OutboundTrampolinePayload {
2784+ impl < ' a > Writeable for OutboundTrampolinePayload < ' a > {
27582785 fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
27592786 match self {
27602787 Self :: Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => {
@@ -2763,6 +2790,41 @@ impl Writeable for OutboundTrampolinePayload {
27632790 ( 4 , HighZeroBytesDroppedBigSize ( * outgoing_cltv_value) , required) ,
27642791 ( 14 , outgoing_node_id, required)
27652792 } ) ;
2793+ } ,
2794+ Self :: LegacyBlindedPathEntry { amt_to_forward, outgoing_cltv_value, payment_paths, invoice_features } => {
2795+ let mut blinded_path_serialization = [ 0u8 ; 2048 ] ; // Fixed-length buffer on the stack
2796+ let serialization_length = {
2797+ let buffer_size = blinded_path_serialization. len ( ) ;
2798+ let mut blinded_path_slice = & mut blinded_path_serialization[ ..] ;
2799+ for current_payment_path in payment_paths {
2800+ current_payment_path. inner_blinded_path ( ) . write ( & mut blinded_path_slice) ?;
2801+ current_payment_path. payinfo . write ( & mut blinded_path_slice) ?;
2802+ }
2803+ buffer_size - blinded_path_slice. len ( )
2804+ } ;
2805+ let blinded_path_serialization = & blinded_path_serialization[ ..serialization_length] ;
2806+ _encode_varint_length_prefixed_tlv ! ( w, {
2807+ ( 2 , HighZeroBytesDroppedBigSize ( * amt_to_forward) , required) ,
2808+ ( 4 , HighZeroBytesDroppedBigSize ( * outgoing_cltv_value) , required) ,
2809+ ( 21 , invoice_features. as_ref( ) . map( |m| WithoutLength ( m) ) , option) ,
2810+ ( 22 , WithoutLength ( blinded_path_serialization) , required)
2811+ } ) ;
2812+ } ,
2813+ Self :: BlindedForward { encrypted_tlvs, intro_node_blinding_point} => {
2814+ _encode_varint_length_prefixed_tlv ! ( w, {
2815+ ( 10 , * * encrypted_tlvs, required_vec) ,
2816+ ( 12 , intro_node_blinding_point, option)
2817+ } ) ;
2818+ } ,
2819+ Self :: BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, intro_node_blinding_point, keysend_preimage, custom_tlvs } => {
2820+ _encode_varint_length_prefixed_tlv ! ( w, {
2821+ ( 2 , HighZeroBytesDroppedBigSize ( * sender_intended_htlc_amt_msat) , required) ,
2822+ ( 4 , HighZeroBytesDroppedBigSize ( * cltv_expiry_height) , required) ,
2823+ ( 10 , * * encrypted_tlvs, required_vec) ,
2824+ ( 12 , intro_node_blinding_point, option) ,
2825+ ( 18 , HighZeroBytesDroppedBigSize ( * total_msat) , required) ,
2826+ ( 20 , keysend_preimage, option)
2827+ } , custom_tlvs. iter( ) ) ;
27662828 }
27672829 }
27682830 Ok ( ( ) )
@@ -3302,7 +3364,7 @@ mod tests {
33023364 use crate :: ln:: types:: ChannelId ;
33033365 use crate :: types:: payment:: { PaymentPreimage , PaymentHash , PaymentSecret } ;
33043366 use crate :: types:: features:: { ChannelFeatures , ChannelTypeFeatures , InitFeatures , NodeFeatures } ;
3305- use crate :: ln:: msgs:: { self , FinalOnionHopData , OnionErrorPacket , CommonOpenChannelFields , CommonAcceptChannelFields , TrampolineOnionPacket } ;
3367+ use crate :: ln:: msgs:: { self , FinalOnionHopData , OnionErrorPacket , CommonOpenChannelFields , CommonAcceptChannelFields , OutboundTrampolinePayload , TrampolineOnionPacket } ;
33063368 use crate :: ln:: msgs:: SocketAddress ;
33073369 use crate :: routing:: gossip:: { NodeAlias , NodeId } ;
33083370 use crate :: util:: ser:: { BigSize , FixedLengthReader , Hostname , LengthReadable , Readable , ReadableArgs , TransactionU16LenLimited , Writeable } ;
@@ -3328,6 +3390,8 @@ mod tests {
33283390
33293391 #[ cfg( feature = "std" ) ]
33303392 use std:: net:: { Ipv4Addr , Ipv6Addr , SocketAddr , SocketAddrV4 , SocketAddrV6 , ToSocketAddrs } ;
3393+ use types:: features:: { BlindedHopFeatures , Bolt12InvoiceFeatures } ;
3394+ use crate :: blinded_path:: payment:: { BlindedPayInfo , BlindedPaymentPath } ;
33313395 #[ cfg( feature = "std" ) ]
33323396 use crate :: ln:: msgs:: SocketAddressParseError ;
33333397
@@ -4675,6 +4739,58 @@ mod tests {
46754739 assert_eq ! ( encoded_trampoline_packet, expected_eclair_trampoline_packet) ;
46764740 }
46774741
4742+ #[ test]
4743+ fn encoding_outbound_trampoline_payload ( ) {
4744+ let mut trampoline_features = Bolt12InvoiceFeatures :: empty ( ) ;
4745+ trampoline_features. set_basic_mpp_optional ( ) ;
4746+ let introduction_node = PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" ) . unwrap ( ) ) . unwrap ( ) ;
4747+ let blinding_point = PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap ( ) ) . unwrap ( ) ;
4748+ let trampoline_payload = OutboundTrampolinePayload :: LegacyBlindedPathEntry {
4749+ amt_to_forward : 150_000_000 ,
4750+ outgoing_cltv_value : 800_000 ,
4751+ payment_paths : vec ! [
4752+ BlindedPaymentPath :: from_raw(
4753+ introduction_node,
4754+ blinding_point,
4755+ vec![ ] ,
4756+ BlindedPayInfo {
4757+ fee_base_msat: 500 ,
4758+ fee_proportional_millionths: 1_000 ,
4759+ cltv_expiry_delta: 36 ,
4760+ htlc_minimum_msat: 1 ,
4761+ htlc_maximum_msat: 500_000_000 ,
4762+ features: BlindedHopFeatures :: empty( ) ,
4763+ }
4764+ )
4765+ ] ,
4766+ invoice_features : Some ( trampoline_features) ,
4767+ } ;
4768+ let serialized_payload = trampoline_payload. encode ( ) . to_lower_hex_string ( ) ;
4769+ assert_eq ! ( serialized_payload, "71020408f0d18004030c35001503020000165f032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e66868099102eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f28368661900000001f4000003e800240000000000000001000000001dcd65000000" ) ;
4770+ }
4771+
4772+ #[ test]
4773+ fn encode_trampoline_blinded_path_payload ( ) {
4774+ let trampoline_payload_eve = OutboundTrampolinePayload :: BlindedReceive {
4775+ sender_intended_htlc_amt_msat : 150_000_000 ,
4776+ total_msat : 150_000_000 ,
4777+ cltv_expiry_height : 800_000 ,
4778+ encrypted_tlvs : & <Vec < u8 > >:: from_hex ( "bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c" ) . unwrap ( ) ,
4779+ intro_node_blinding_point : None ,
4780+ keysend_preimage : None ,
4781+ custom_tlvs : & vec ! [ ] ,
4782+ } ;
4783+ let eve_payload = trampoline_payload_eve. encode ( ) . to_lower_hex_string ( ) ;
4784+ assert_eq ! ( eve_payload, "e4020408f0d18004030c35000ad1bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c120408f0d180" ) ;
4785+
4786+ let trampoline_payload_dave = OutboundTrampolinePayload :: BlindedForward {
4787+ encrypted_tlvs : & <Vec < u8 > >:: from_hex ( "0ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a" ) . unwrap ( ) ,
4788+ intro_node_blinding_point : Some ( PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e" ) . unwrap ( ) ) . unwrap ( ) ) ,
4789+ } ;
4790+ let dave_payload = trampoline_payload_dave. encode ( ) . to_lower_hex_string ( ) ;
4791+ assert_eq ! ( dave_payload, "690a440ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a0c2102988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e" )
4792+ }
4793+
46784794 #[ test]
46794795 fn query_channel_range_end_blocknum ( ) {
46804796 let tests: Vec < ( u32 , u32 , u32 ) > = vec ! [
0 commit comments