@@ -1144,6 +1144,10 @@ where
1144
1144
. expect ( "Route we used spontaneously grew invalid keys in the middle of it?" ) ;
1145
1145
}
1146
1146
1147
+ // In the best case, paths can be up to 27 hops. But attribution data can only be conveyed back to the sender from
1148
+ // the first 20 hops. Determine the number of hops to be used for attribution data.
1149
+ let attributable_hop_count = usize:: min ( path. hops . len ( ) , MAX_HOPS ) ;
1150
+
1147
1151
// Handle packed channel/node updates for passing back for the route handler
1148
1152
let mut iterator = onion_keys. into_iter ( ) . enumerate ( ) . peekable ( ) ;
1149
1153
while let Some ( ( route_hop_idx, ( route_hop_option, shared_secret) ) ) = iterator. next ( ) {
@@ -1215,11 +1219,13 @@ where
1215
1219
// Check attr error HMACs if present.
1216
1220
if let Some ( ref mut attribution_data) = encrypted_packet. attribution_data {
1217
1221
// Only consider hops in the regular path for attribution data. Failures in a blinded path are not attributable.
1218
- if route_hop_idx < path. hops . len ( ) {
1219
- // Calculate position relative to the final hop. The final hop is at position 0. The failure node does not
1220
- // need to come from the final node, but we need to look at the chain of HMACs that does include all data up
1221
- // to the final node. For a more nearby failure, the verified HMACs will include some zero padding data.
1222
- let position = path. hops . len ( ) - route_hop_idx - 1 ;
1222
+ if route_hop_idx < attributable_hop_count {
1223
+ // Calculate position relative to the last attributable hop. The last attributable hop is at position 0.
1224
+ // The failure node does not need to come from the last attributable hop, but we need to look at the
1225
+ // chain of HMACs that does include all data up to the last attributable hop. For a more nearby failure,
1226
+ // the verified HMACs will include some zero padding data. Failures beyond the last attributable hop
1227
+ // will not be attributable.
1228
+ let position = attributable_hop_count - route_hop_idx - 1 ;
1223
1229
let hold_time = attribution_data. verify (
1224
1230
& encrypted_packet. data ,
1225
1231
shared_secret. as_ref ( ) ,
@@ -3169,6 +3175,79 @@ mod tests {
3169
3175
assert_eq ! ( decrypted_failure. short_channel_id, Some ( 0 ) ) ;
3170
3176
}
3171
3177
3178
+ #[ test]
3179
+ fn test_long_route_attributable_failure ( ) {
3180
+ // Test a long route that exceeds the reach of attribution data.
3181
+
3182
+ let secp_ctx = Secp256k1 :: new ( ) ;
3183
+ const LEGACY_MAX_HOPS : usize = 27 ;
3184
+
3185
+ // Construct a route with 27 hops.
3186
+ let mut hops = Vec :: new ( ) ;
3187
+ for i in 0 ..LEGACY_MAX_HOPS {
3188
+ let mut secret_bytes = [ 0 ; 32 ] ;
3189
+ secret_bytes[ 0 ] = ( i + 1 ) as u8 ;
3190
+ let secret_key = SecretKey :: from_slice ( & secret_bytes) . unwrap ( ) ;
3191
+ let pubkey = secret_key. public_key ( & secp_ctx) ;
3192
+
3193
+ hops. push ( RouteHop {
3194
+ pubkey,
3195
+ channel_features : ChannelFeatures :: empty ( ) ,
3196
+ node_features : NodeFeatures :: empty ( ) ,
3197
+ short_channel_id : i as u64 ,
3198
+ fee_msat : 0 ,
3199
+ cltv_expiry_delta : 0 ,
3200
+ maybe_announced_channel : true ,
3201
+ } ) ;
3202
+ }
3203
+ let path = Path { hops, blinded_tail : None } ;
3204
+
3205
+ // Calculate shared secrets.
3206
+ let mut onion_keys = Vec :: new ( ) ;
3207
+ let session_key = get_test_session_key ( ) ;
3208
+ construct_onion_keys_generic_callback (
3209
+ & secp_ctx,
3210
+ & path. hops ,
3211
+ None ,
3212
+ & session_key,
3213
+ |shared_secret, _, _, _, _| onion_keys. push ( shared_secret) ,
3214
+ )
3215
+ . unwrap ( ) ;
3216
+
3217
+ // Construct the htlc source.
3218
+ let logger = TestLogger :: new ( ) ;
3219
+ let htlc_source = HTLCSource :: OutboundRoute {
3220
+ path,
3221
+ session_priv : session_key,
3222
+ first_hop_htlc_msat : 0 ,
3223
+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
3224
+ } ;
3225
+
3226
+ // Iterate over all possible failure positions and check that the cases that can be attributed are.
3227
+ for failure_pos in 0 ..LEGACY_MAX_HOPS {
3228
+ // Create a failure packet with bogus data.
3229
+ let packet = vec ! [ 1u8 ; 292 ] ;
3230
+ let mut onion_error =
3231
+ OnionErrorPacket { data : packet, attribution_data : Some ( AttributionData :: new ( ) ) } ;
3232
+
3233
+ // Apply the processing that the preceding hops would apply.
3234
+ for i in ( 0 ..failure_pos) . rev ( ) {
3235
+ let shared_secret = onion_keys[ i] . secret_bytes ( ) ;
3236
+ process_failure_packet ( & mut onion_error, & shared_secret, 0 ) ;
3237
+ super :: crypt_failure_packet ( & shared_secret, & mut onion_error) ;
3238
+ }
3239
+
3240
+ // Decrypt the failure.
3241
+ let decrypted_failure =
3242
+ process_onion_failure ( & secp_ctx, & & logger, & htlc_source, onion_error) ;
3243
+
3244
+ // Expect attribution up to hop 20.
3245
+ let expected_failed_chan =
3246
+ if failure_pos < MAX_HOPS { Some ( failure_pos as u64 ) } else { None } ;
3247
+ assert_eq ! ( decrypted_failure. short_channel_id, expected_failed_chan) ;
3248
+ }
3249
+ }
3250
+
3172
3251
#[ test]
3173
3252
fn test_unreadable_failure_packet_onion ( ) {
3174
3253
// Create a failure packet with a valid hmac but unreadable failure message.
0 commit comments