@@ -931,15 +931,11 @@ pub(crate) struct DecodedOnionFailure {
931931 pub ( crate ) onion_error_data : Option < Vec < u8 > > ,
932932}
933933
934- /// Note that we always decrypt `packet` in-place here even if the deserialization into
935- /// [`msgs::DecodedOnionErrorPacket`] ultimately fails.
936- fn decrypt_onion_error_packet (
937- packet : & mut Vec < u8 > , shared_secret : SharedSecret ,
938- ) -> Result < msgs:: DecodedOnionErrorPacket , msgs:: DecodeError > {
934+ /// Decrypt the error packet in-place.
935+ fn decrypt_onion_error_packet ( packet : & mut Vec < u8 > , shared_secret : SharedSecret ) {
939936 let ammag = gen_ammag_from_shared_secret ( shared_secret. as_ref ( ) ) ;
940937 let mut chacha = ChaCha20 :: new ( & ammag, & [ 0u8 ; 8 ] ) ;
941938 chacha. process_in_place ( packet) ;
942- msgs:: DecodedOnionErrorPacket :: read ( & mut Cursor :: new ( packet) )
943939}
944940
945941/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
@@ -1021,9 +1017,11 @@ where
10211017 {
10221018 // Actually parse the onion error data in tests so we can check that blinded hops fail
10231019 // back correctly.
1024- let err_packet =
1025- decrypt_onion_error_packet ( & mut encrypted_packet, shared_secret)
1026- . unwrap ( ) ;
1020+ decrypt_onion_error_packet ( & mut encrypted_packet, shared_secret) ;
1021+ let err_packet = msgs:: DecodedOnionErrorPacket :: read ( & mut Cursor :: new (
1022+ & encrypted_packet,
1023+ ) )
1024+ . unwrap ( ) ;
10271025 error_code_ret = Some ( u16:: from_be_bytes (
10281026 err_packet. failuremsg . get ( 0 ..2 ) . unwrap ( ) . try_into ( ) . unwrap ( ) ,
10291027 ) ) ;
@@ -1044,22 +1042,44 @@ where
10441042 let amt_to_forward = htlc_msat - route_hop. fee_msat ;
10451043 htlc_msat = amt_to_forward;
10461044
1047- let err_packet = match decrypt_onion_error_packet ( & mut encrypted_packet, shared_secret) {
1048- Ok ( p) => p,
1049- Err ( _) => return ,
1050- } ;
1045+ decrypt_onion_error_packet ( & mut encrypted_packet, shared_secret) ;
1046+
10511047 let um = gen_um_from_shared_secret ( shared_secret. as_ref ( ) ) ;
10521048 let mut hmac = HmacEngine :: < Sha256 > :: new ( & um) ;
1053- hmac. input ( & err_packet . encode ( ) [ 32 ..] ) ;
1049+ hmac. input ( & encrypted_packet [ 32 ..] ) ;
10541050
1055- if !fixed_time_eq ( & Hmac :: from_engine ( hmac) . to_byte_array ( ) , & err_packet . hmac ) {
1051+ if !fixed_time_eq ( & Hmac :: from_engine ( hmac) . to_byte_array ( ) , & encrypted_packet [ .. 32 ] ) {
10561052 return ;
10571053 }
1054+
1055+ let err_packet =
1056+ match msgs:: DecodedOnionErrorPacket :: read ( & mut Cursor :: new ( & encrypted_packet) ) {
1057+ Ok ( p) => p,
1058+ Err ( _) => {
1059+ log_warn ! ( logger, "Unreadable failure from {}" , route_hop. pubkey) ;
1060+
1061+ let network_update = Some ( NetworkUpdate :: NodeFailure {
1062+ node_id : route_hop. pubkey ,
1063+ is_permanent : true ,
1064+ } ) ;
1065+ let short_channel_id = Some ( route_hop. short_channel_id ) ;
1066+ res = Some ( FailureLearnings {
1067+ network_update,
1068+ short_channel_id,
1069+ payment_failed_permanently : is_from_final_node,
1070+ failed_within_blinded_path : false ,
1071+ } ) ;
1072+ return ;
1073+ } ,
1074+ } ;
1075+
10581076 let error_code_slice = match err_packet. failuremsg . get ( 0 ..2 ) {
10591077 Some ( s) => s,
10601078 None => {
10611079 // Useless packet that we can't use but it passed HMAC, so it definitely came from the peer
10621080 // in question
1081+ log_warn ! ( logger, "Missing error code in failure from {}" , route_hop. pubkey) ;
1082+
10631083 let network_update = Some ( NetworkUpdate :: NodeFailure {
10641084 node_id : route_hop. pubkey ,
10651085 is_permanent : true ,
@@ -1219,6 +1239,12 @@ where
12191239 } else {
12201240 // only not set either packet unparseable or hmac does not match with any
12211241 // payment not retryable only when garbage is from the final node
1242+ log_warn ! (
1243+ logger,
1244+ "Non-attributable failure encountered on route {}" ,
1245+ path. hops. iter( ) . map( |h| h. pubkey. to_string( ) ) . collect:: <Vec <_>>( ) . join( "->" )
1246+ ) ;
1247+
12221248 DecodedOnionFailure {
12231249 network_update : None ,
12241250 short_channel_id : None ,
@@ -1764,7 +1790,10 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(
17641790
17651791#[ cfg( test) ]
17661792mod tests {
1793+ use std:: sync:: Arc ;
1794+
17671795 use crate :: io;
1796+ use crate :: ln:: channelmanager:: PaymentId ;
17681797 use crate :: ln:: msgs;
17691798 use crate :: routing:: router:: { Path , PaymentParameters , Route , RouteHop } ;
17701799 use crate :: types:: features:: { ChannelFeatures , NodeFeatures } ;
@@ -1773,6 +1802,7 @@ mod tests {
17731802
17741803 #[ allow( unused_imports) ]
17751804 use crate :: prelude:: * ;
1805+ use crate :: util:: test_utils:: TestLogger ;
17761806
17771807 use bitcoin:: hex:: FromHex ;
17781808 use bitcoin:: secp256k1:: Secp256k1 ;
@@ -1785,40 +1815,95 @@ mod tests {
17851815 SecretKey :: from_slice ( & <Vec < u8 > >:: from_hex ( hex) . unwrap ( ) [ ..] ) . unwrap ( )
17861816 }
17871817
1818+ fn build_test_path ( ) -> Path {
1819+ Path {
1820+ hops : vec ! [
1821+ RouteHop {
1822+ pubkey: PublicKey :: from_slice(
1823+ & <Vec <u8 >>:: from_hex(
1824+ "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ,
1825+ )
1826+ . unwrap( ) [ ..] ,
1827+ )
1828+ . unwrap( ) ,
1829+ channel_features: ChannelFeatures :: empty( ) ,
1830+ node_features: NodeFeatures :: empty( ) ,
1831+ short_channel_id: 0 ,
1832+ fee_msat: 0 ,
1833+ cltv_expiry_delta: 0 ,
1834+ maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1835+ } ,
1836+ RouteHop {
1837+ pubkey: PublicKey :: from_slice(
1838+ & <Vec <u8 >>:: from_hex(
1839+ "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ,
1840+ )
1841+ . unwrap( ) [ ..] ,
1842+ )
1843+ . unwrap( ) ,
1844+ channel_features: ChannelFeatures :: empty( ) ,
1845+ node_features: NodeFeatures :: empty( ) ,
1846+ short_channel_id: 1 ,
1847+ fee_msat: 0 ,
1848+ cltv_expiry_delta: 0 ,
1849+ maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1850+ } ,
1851+ RouteHop {
1852+ pubkey: PublicKey :: from_slice(
1853+ & <Vec <u8 >>:: from_hex(
1854+ "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ,
1855+ )
1856+ . unwrap( ) [ ..] ,
1857+ )
1858+ . unwrap( ) ,
1859+ channel_features: ChannelFeatures :: empty( ) ,
1860+ node_features: NodeFeatures :: empty( ) ,
1861+ short_channel_id: 2 ,
1862+ fee_msat: 0 ,
1863+ cltv_expiry_delta: 0 ,
1864+ maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1865+ } ,
1866+ RouteHop {
1867+ pubkey: PublicKey :: from_slice(
1868+ & <Vec <u8 >>:: from_hex(
1869+ "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" ,
1870+ )
1871+ . unwrap( ) [ ..] ,
1872+ )
1873+ . unwrap( ) ,
1874+ channel_features: ChannelFeatures :: empty( ) ,
1875+ node_features: NodeFeatures :: empty( ) ,
1876+ short_channel_id: 3 ,
1877+ fee_msat: 0 ,
1878+ cltv_expiry_delta: 0 ,
1879+ maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1880+ } ,
1881+ RouteHop {
1882+ pubkey: PublicKey :: from_slice(
1883+ & <Vec <u8 >>:: from_hex(
1884+ "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145" ,
1885+ )
1886+ . unwrap( ) [ ..] ,
1887+ )
1888+ . unwrap( ) ,
1889+ channel_features: ChannelFeatures :: empty( ) ,
1890+ node_features: NodeFeatures :: empty( ) ,
1891+ short_channel_id: 4 ,
1892+ fee_msat: 0 ,
1893+ cltv_expiry_delta: 0 ,
1894+ maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1895+ } ,
1896+ ] ,
1897+ blinded_tail : None ,
1898+ }
1899+ }
1900+
17881901 fn build_test_onion_keys ( ) -> Vec < OnionKeys > {
17891902 // Keys from BOLT 4, used in both test vector tests
17901903 let secp_ctx = Secp256k1 :: new ( ) ;
17911904
1792- let route = Route {
1793- paths : vec ! [ Path { hops: vec![
1794- RouteHop {
1795- pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
1796- channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
1797- short_channel_id: 0 , fee_msat: 0 , cltv_expiry_delta: 0 , maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1798- } ,
1799- RouteHop {
1800- pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
1801- channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
1802- short_channel_id: 0 , fee_msat: 0 , cltv_expiry_delta: 0 , maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1803- } ,
1804- RouteHop {
1805- pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) [ ..] ) . unwrap( ) ,
1806- channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
1807- short_channel_id: 0 , fee_msat: 0 , cltv_expiry_delta: 0 , maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1808- } ,
1809- RouteHop {
1810- pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" ) . unwrap( ) [ ..] ) . unwrap( ) ,
1811- channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
1812- short_channel_id: 0 , fee_msat: 0 , cltv_expiry_delta: 0 , maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1813- } ,
1814- RouteHop {
1815- pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145" ) . unwrap( ) [ ..] ) . unwrap( ) ,
1816- channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
1817- short_channel_id: 0 , fee_msat: 0 , cltv_expiry_delta: 0 , maybe_announced_channel: true , // We fill in the payloads manually instead of generating them from RouteHops.
1818- } ,
1819- ] , blinded_tail: None } ] ,
1820- route_params : None ,
1821- } ;
1905+ let path = build_test_path ( ) ;
1906+ let route = Route { paths : vec ! [ path] , route_params : None } ;
18221907
18231908 let onion_keys =
18241909 super :: construct_onion_keys ( & secp_ctx, & route. paths [ 0 ] , & get_test_session_key ( ) )
@@ -2078,6 +2163,108 @@ mod tests {
20782163 ) ;
20792164 let hex = "9c5add3963fc7f6ed7f148623c84134b5647e1306419dbe2174e523fa9e2fbed3a06a19f899145610741c83ad40b7712aefaddec8c6baf7325d92ea4ca4d1df8bce517f7e54554608bf2bd8071a4f52a7a2f7ffbb1413edad81eeea5785aa9d990f2865dc23b4bc3c301a94eec4eabebca66be5cf638f693ec256aec514620cc28ee4a94bd9565bc4d4962b9d3641d4278fb319ed2b84de5b665f307a2db0f7fbb757366067d88c50f7e829138fde4f78d39b5b5802f1b92a8a820865af5cc79f9f30bc3f461c66af95d13e5e1f0381c184572a91dee1c849048a647a1158cf884064deddbf1b0b88dfe2f791428d0ba0f6fb2f04e14081f69165ae66d9297c118f0907705c9c4954a199bae0bb96fad763d690e7daa6cfda59ba7f2c8d11448b604d12d" ;
20802165 assert_eq ! ( onion_packet_5. data, <Vec <u8 >>:: from_hex( hex) . unwrap( ) ) ;
2166+
2167+ let logger: Arc < TestLogger > = Arc :: new ( TestLogger :: new ( ) ) ;
2168+ let ctx_full = Secp256k1 :: new ( ) ;
2169+ let path = build_test_path ( ) ;
2170+ let htlc_source = HTLCSource :: OutboundRoute {
2171+ path,
2172+ session_priv : get_test_session_key ( ) ,
2173+ first_hop_htlc_msat : 0 ,
2174+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2175+ } ;
2176+
2177+ // Assert that the original failure can be retrieved and that all hmacs check out.
2178+ let decrypted_failure =
2179+ process_onion_failure ( & ctx_full, & logger, & htlc_source, onion_packet_5. data ) ;
2180+
2181+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x2002 ) ) ;
2182+ }
2183+
2184+ #[ test]
2185+ fn test_non_attributable_failure_packet_onion ( ) {
2186+ // Create a failure packet with bogus data.
2187+ let packet = vec ! [ 1u8 ; 292 ] ;
2188+
2189+ // In the current protocol, it is unfortunately not possible to identify the failure source.
2190+ let logger: TestLogger = TestLogger :: new ( ) ;
2191+ let decrypted_failure = test_failure_attribution ( & logger, & packet) ;
2192+ assert_eq ! ( decrypted_failure. short_channel_id, None ) ;
2193+
2194+ logger. assert_log_contains (
2195+ "lightning::ln::onion_utils" ,
2196+ "Non-attributable failure encountered" ,
2197+ 1 ,
2198+ ) ;
2199+ }
2200+
2201+ #[ test]
2202+ fn test_unreadable_failure_packet_onion ( ) {
2203+ // Create a failure packet with a valid hmac but unreadable failure message.
2204+ let onion_keys: Vec < OnionKeys > = build_test_onion_keys ( ) ;
2205+ let shared_secret = onion_keys[ 0 ] . shared_secret . as_ref ( ) ;
2206+ let um = gen_um_from_shared_secret ( & shared_secret) ;
2207+
2208+ // The failure message is a single 0 byte.
2209+ let mut packet = [ 0u8 ; 33 ] ;
2210+
2211+ let mut hmac = HmacEngine :: < Sha256 > :: new ( & um) ;
2212+ hmac. input ( & packet[ 32 ..] ) ;
2213+ let hmac = Hmac :: from_engine ( hmac) . to_byte_array ( ) ;
2214+ packet[ ..32 ] . copy_from_slice ( & hmac) ;
2215+
2216+ let packet = encrypt_failure_packet ( shared_secret, & packet) ;
2217+
2218+ // For the unreadable failure, it is still expected that the failing channel can be identified.
2219+ let logger: TestLogger = TestLogger :: new ( ) ;
2220+ let decrypted_failure = test_failure_attribution ( & logger, & packet. data ) ;
2221+ assert_eq ! ( decrypted_failure. short_channel_id, Some ( 0 ) ) ;
2222+
2223+ logger. assert_log_contains ( "lightning::ln::onion_utils" , "Unreadable failure" , 1 ) ;
2224+ }
2225+
2226+ #[ test]
2227+ fn test_missing_error_code ( ) {
2228+ // Create a failure packet with a valid hmac and structure, but no error code.
2229+ let onion_keys: Vec < OnionKeys > = build_test_onion_keys ( ) ;
2230+ let shared_secret = onion_keys[ 0 ] . shared_secret . as_ref ( ) ;
2231+ let um = gen_um_from_shared_secret ( & shared_secret) ;
2232+
2233+ let failuremsg = vec ! [ 1 ] ;
2234+ let pad = Vec :: new ( ) ;
2235+ let mut packet = msgs:: DecodedOnionErrorPacket { hmac : [ 0 ; 32 ] , failuremsg, pad } ;
2236+
2237+ let mut hmac = HmacEngine :: < Sha256 > :: new ( & um) ;
2238+ hmac. input ( & packet. encode ( ) [ 32 ..] ) ;
2239+ packet. hmac = Hmac :: from_engine ( hmac) . to_byte_array ( ) ;
2240+
2241+ let packet = encrypt_failure_packet ( shared_secret, & packet. encode ( ) [ ..] ) ;
2242+
2243+ let logger = TestLogger :: new ( ) ;
2244+ let decrypted_failure = test_failure_attribution ( & logger, & packet. data ) ;
2245+ assert_eq ! ( decrypted_failure. short_channel_id, Some ( 0 ) ) ;
2246+
2247+ logger. assert_log_contains (
2248+ "lightning::ln::onion_utils" ,
2249+ "Missing error code in failure" ,
2250+ 1 ,
2251+ ) ;
2252+ }
2253+
2254+ fn test_failure_attribution ( logger : & TestLogger , packet : & [ u8 ] ) -> DecodedOnionFailure {
2255+ let ctx_full = Secp256k1 :: new ( ) ;
2256+ let path = build_test_path ( ) ;
2257+ let htlc_source = HTLCSource :: OutboundRoute {
2258+ path,
2259+ session_priv : get_test_session_key ( ) ,
2260+ first_hop_htlc_msat : 0 ,
2261+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2262+ } ;
2263+
2264+ let decrypted_failure =
2265+ process_onion_failure ( & ctx_full, & logger, & htlc_source, packet. into ( ) ) ;
2266+
2267+ decrypted_failure
20812268 }
20822269
20832270 struct RawOnionHopData {
0 commit comments