@@ -931,15 +931,11 @@ pub(crate) struct DecodedOnionFailure {
931
931
pub ( crate ) onion_error_data : Option < Vec < u8 > > ,
932
932
}
933
933
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 ) {
939
936
let ammag = gen_ammag_from_shared_secret ( shared_secret. as_ref ( ) ) ;
940
937
let mut chacha = ChaCha20 :: new ( & ammag, & [ 0u8 ; 8 ] ) ;
941
938
chacha. process_in_place ( packet) ;
942
- msgs:: DecodedOnionErrorPacket :: read ( & mut Cursor :: new ( packet) )
943
939
}
944
940
945
941
/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
@@ -1021,9 +1017,11 @@ where
1021
1017
{
1022
1018
// Actually parse the onion error data in tests so we can check that blinded hops fail
1023
1019
// 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 ( ) ;
1027
1025
error_code_ret = Some ( u16:: from_be_bytes (
1028
1026
err_packet. failuremsg . get ( 0 ..2 ) . unwrap ( ) . try_into ( ) . unwrap ( ) ,
1029
1027
) ) ;
@@ -1044,22 +1042,44 @@ where
1044
1042
let amt_to_forward = htlc_msat - route_hop. fee_msat ;
1045
1043
htlc_msat = amt_to_forward;
1046
1044
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
+
1051
1047
let um = gen_um_from_shared_secret ( shared_secret. as_ref ( ) ) ;
1052
1048
let mut hmac = HmacEngine :: < Sha256 > :: new ( & um) ;
1053
- hmac. input ( & err_packet . encode ( ) [ 32 ..] ) ;
1049
+ hmac. input ( & encrypted_packet [ 32 ..] ) ;
1054
1050
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 ] ) {
1056
1052
return ;
1057
1053
}
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
+
1058
1076
let error_code_slice = match err_packet. failuremsg . get ( 0 ..2 ) {
1059
1077
Some ( s) => s,
1060
1078
None => {
1061
1079
// Useless packet that we can't use but it passed HMAC, so it definitely came from the peer
1062
1080
// in question
1081
+ log_warn ! ( logger, "Missing error code in failure from {}" , route_hop. pubkey) ;
1082
+
1063
1083
let network_update = Some ( NetworkUpdate :: NodeFailure {
1064
1084
node_id : route_hop. pubkey ,
1065
1085
is_permanent : true ,
@@ -1219,6 +1239,12 @@ where
1219
1239
} else {
1220
1240
// only not set either packet unparseable or hmac does not match with any
1221
1241
// 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
+
1222
1248
DecodedOnionFailure {
1223
1249
network_update : None ,
1224
1250
short_channel_id : None ,
@@ -1764,7 +1790,10 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(
1764
1790
1765
1791
#[ cfg( test) ]
1766
1792
mod tests {
1793
+ use std:: sync:: Arc ;
1794
+
1767
1795
use crate :: io;
1796
+ use crate :: ln:: channelmanager:: PaymentId ;
1768
1797
use crate :: ln:: msgs;
1769
1798
use crate :: routing:: router:: { Path , PaymentParameters , Route , RouteHop } ;
1770
1799
use crate :: types:: features:: { ChannelFeatures , NodeFeatures } ;
@@ -1773,6 +1802,7 @@ mod tests {
1773
1802
1774
1803
#[ allow( unused_imports) ]
1775
1804
use crate :: prelude:: * ;
1805
+ use crate :: util:: test_utils:: TestLogger ;
1776
1806
1777
1807
use bitcoin:: hex:: FromHex ;
1778
1808
use bitcoin:: secp256k1:: Secp256k1 ;
@@ -1785,40 +1815,95 @@ mod tests {
1785
1815
SecretKey :: from_slice ( & <Vec < u8 > >:: from_hex ( hex) . unwrap ( ) [ ..] ) . unwrap ( )
1786
1816
}
1787
1817
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
+
1788
1901
fn build_test_onion_keys ( ) -> Vec < OnionKeys > {
1789
1902
// Keys from BOLT 4, used in both test vector tests
1790
1903
let secp_ctx = Secp256k1 :: new ( ) ;
1791
1904
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 } ;
1822
1907
1823
1908
let onion_keys =
1824
1909
super :: construct_onion_keys ( & secp_ctx, & route. paths [ 0 ] , & get_test_session_key ( ) )
@@ -2078,6 +2163,108 @@ mod tests {
2078
2163
) ;
2079
2164
let hex = "9c5add3963fc7f6ed7f148623c84134b5647e1306419dbe2174e523fa9e2fbed3a06a19f899145610741c83ad40b7712aefaddec8c6baf7325d92ea4ca4d1df8bce517f7e54554608bf2bd8071a4f52a7a2f7ffbb1413edad81eeea5785aa9d990f2865dc23b4bc3c301a94eec4eabebca66be5cf638f693ec256aec514620cc28ee4a94bd9565bc4d4962b9d3641d4278fb319ed2b84de5b665f307a2db0f7fbb757366067d88c50f7e829138fde4f78d39b5b5802f1b92a8a820865af5cc79f9f30bc3f461c66af95d13e5e1f0381c184572a91dee1c849048a647a1158cf884064deddbf1b0b88dfe2f791428d0ba0f6fb2f04e14081f69165ae66d9297c118f0907705c9c4954a199bae0bb96fad763d690e7daa6cfda59ba7f2c8d11448b604d12d" ;
2080
2165
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
2081
2268
}
2082
2269
2083
2270
struct RawOnionHopData {
0 commit comments