@@ -284,13 +284,6 @@ func deriveBlindedRouteForwardingInfo(r *sphinxHopIterator,
284284 return nil , routeRole , err
285285 }
286286
287- nextSCID , err := routeData .ShortChannelID .UnwrapOrErr (
288- fmt .Errorf ("next SCID not set for non-final blinded hop" ),
289- )
290- if err != nil {
291- return nil , routeRole , err
292- }
293-
294287 fwdAmt , err := calculateForwardingAmount (
295288 r .blindingKit .IncomingAmount , relayInfo .Val .BaseFee ,
296289 relayInfo .Val .FeeRate ,
@@ -315,6 +308,22 @@ func deriveBlindedRouteForwardingInfo(r *sphinxHopIterator,
315308 return nil , routeRole , err
316309 }
317310
311+ // If the payload signals that the following hop is a dummy hop, then
312+ // we will iteratively peel the dummy hop until we reach the final
313+ // payload.
314+ if checkForDummyHop (routeData , r .router .OnionPublicKey ()) {
315+ return peelBlindedPathDummyHop (
316+ r , uint32 (relayInfo .Val .CltvExpiryDelta ), fwdAmt ,
317+ routeRole , nextEph ,
318+ )
319+ }
320+
321+ nextSCID , err := routeData .ShortChannelID .UnwrapOrErr (
322+ fmt .Errorf ("next SCID not set for non-final blinded hop" ),
323+ )
324+ if err != nil {
325+ return nil , routeRole , err
326+ }
318327 payload .FwdInfo = ForwardingInfo {
319328 NextHop : nextSCID .Val ,
320329 AmountToForward : fwdAmt ,
@@ -332,6 +341,55 @@ func deriveBlindedRouteForwardingInfo(r *sphinxHopIterator,
332341 return payload , routeRole , nil
333342}
334343
344+ // checkForDummyHop returns whether the given BlindedRouteData packet indicates
345+ // the presence of a dummy hop.
346+ func checkForDummyHop (routeData * record.BlindedRouteData ,
347+ routerPubKey * btcec.PublicKey ) bool {
348+
349+ var isDummy bool
350+ routeData .NextNodeID .WhenSome (
351+ func (r tlv.RecordT [tlv.TlvType4 , * btcec.PublicKey ]) {
352+ isDummy = r .Val .IsEqual (routerPubKey )
353+ },
354+ )
355+
356+ return isDummy
357+ }
358+
359+ // peelBlindedPathDummyHop packages the next onion packet and then constructs
360+ // a new hop iterator using our router and then proceeds to process the next
361+ // packet. This can only be done for blinded route dummy hops since we expect
362+ // to be the final hop on the path.
363+ func peelBlindedPathDummyHop (r * sphinxHopIterator , cltvExpiryDelta uint32 ,
364+ fwdAmt lnwire.MilliSatoshi , routeRole RouteRole ,
365+ nextEph tlv.RecordT [tlv.TlvType8 , * btcec.PublicKey ]) (* Payload ,
366+ RouteRole , error ) {
367+
368+ onionPkt := r .processedPacket .NextPacket
369+ sphinxPacket , err := r .router .ReconstructOnionPacket (
370+ onionPkt , r .rHash , sphinx .WithBlindingPoint (nextEph .Val ),
371+ )
372+ if err != nil {
373+ return nil , routeRole , err
374+ }
375+
376+ iterator := makeSphinxHopIterator (
377+ r .router , onionPkt , sphinxPacket , BlindingKit {
378+ Processor : r .router ,
379+ UpdateAddBlinding : tlv .SomeRecordT (
380+ tlv.NewPrimitiveRecord [lnwire.BlindingPointTlvType ]( //nolint:lll
381+ nextEph .Val ,
382+ ),
383+ ),
384+ IncomingAmount : fwdAmt ,
385+ IncomingCltv : r .blindingKit .IncomingCltv -
386+ cltvExpiryDelta ,
387+ }, r .rHash ,
388+ )
389+
390+ return extractTLVPayload (iterator )
391+ }
392+
335393// decryptAndValidateBlindedRouteData decrypts the encrypted payload from the
336394// payment recipient using a blinding key. The incoming HTLC amount and CLTV
337395// values are then verified against the policy values from the recipient.
0 commit comments