@@ -1746,9 +1746,9 @@ pub struct FinalOnionHopData {
1746
1746
1747
1747
mod fuzzy_internal_msgs {
1748
1748
use bitcoin:: secp256k1:: PublicKey ;
1749
- use crate :: blinded_path:: payment:: { PaymentConstraints , PaymentContext , PaymentRelay } ;
1749
+ use crate :: blinded_path:: payment:: { BlindedPaymentPath , PaymentConstraints , PaymentContext , PaymentRelay } ;
1750
1750
use crate :: types:: payment:: { PaymentPreimage , PaymentSecret } ;
1751
- use crate :: types:: features:: BlindedHopFeatures ;
1751
+ use crate :: types:: features:: { BlindedHopFeatures , Bolt12InvoiceFeatures } ;
1752
1752
use super :: { FinalOnionHopData , TrampolineOnionPacket } ;
1753
1753
1754
1754
#[ allow( unused_imports) ]
@@ -1830,14 +1830,41 @@ mod fuzzy_internal_msgs {
1830
1830
}
1831
1831
}
1832
1832
1833
- pub ( crate ) enum OutboundTrampolinePayload {
1833
+ pub ( crate ) enum OutboundTrampolinePayload < ' a > {
1834
1834
#[ allow( unused) ]
1835
1835
Forward {
1836
1836
/// The value, in msat, of the payment after this hop's fee is deducted.
1837
1837
amt_to_forward : u64 ,
1838
1838
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.
1840
1840
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 > ) > ,
1841
1868
}
1842
1869
}
1843
1870
@@ -2754,7 +2781,7 @@ impl<'a> Writeable for OutboundOnionPayload<'a> {
2754
2781
}
2755
2782
}
2756
2783
2757
- impl Writeable for OutboundTrampolinePayload {
2784
+ impl < ' a > Writeable for OutboundTrampolinePayload < ' a > {
2758
2785
fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
2759
2786
match self {
2760
2787
Self :: Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => {
@@ -2763,6 +2790,41 @@ impl Writeable for OutboundTrampolinePayload {
2763
2790
( 4 , HighZeroBytesDroppedBigSize ( * outgoing_cltv_value) , required) ,
2764
2791
( 14 , outgoing_node_id, required)
2765
2792
} ) ;
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( ) ) ;
2766
2828
}
2767
2829
}
2768
2830
Ok ( ( ) )
@@ -3302,7 +3364,7 @@ mod tests {
3302
3364
use crate :: ln:: types:: ChannelId ;
3303
3365
use crate :: types:: payment:: { PaymentPreimage , PaymentHash , PaymentSecret } ;
3304
3366
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 } ;
3306
3368
use crate :: ln:: msgs:: SocketAddress ;
3307
3369
use crate :: routing:: gossip:: { NodeAlias , NodeId } ;
3308
3370
use crate :: util:: ser:: { BigSize , FixedLengthReader , Hostname , LengthReadable , Readable , ReadableArgs , TransactionU16LenLimited , Writeable } ;
@@ -3328,6 +3390,8 @@ mod tests {
3328
3390
3329
3391
#[ cfg( feature = "std" ) ]
3330
3392
use std:: net:: { Ipv4Addr , Ipv6Addr , SocketAddr , SocketAddrV4 , SocketAddrV6 , ToSocketAddrs } ;
3393
+ use types:: features:: { BlindedHopFeatures , Bolt12InvoiceFeatures } ;
3394
+ use crate :: blinded_path:: payment:: { BlindedPayInfo , BlindedPaymentPath } ;
3331
3395
#[ cfg( feature = "std" ) ]
3332
3396
use crate :: ln:: msgs:: SocketAddressParseError ;
3333
3397
@@ -4675,6 +4739,58 @@ mod tests {
4675
4739
assert_eq ! ( encoded_trampoline_packet, expected_eclair_trampoline_packet) ;
4676
4740
}
4677
4741
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
+
4678
4794
#[ test]
4679
4795
fn query_channel_range_end_blocknum ( ) {
4680
4796
let tests: Vec < ( u32 , u32 , u32 ) > = vec ! [
0 commit comments