diff --git a/htlcswitch/hop/forwarding_info.go b/htlcswitch/hop/forwarding_info.go index 3ec358a0a..5a1463c48 100644 --- a/htlcswitch/hop/forwarding_info.go +++ b/htlcswitch/hop/forwarding_info.go @@ -22,4 +22,9 @@ type ForwardingInfo struct { // OutgoingCTLV is the specified value of the CTLV timelock to be used // in the outgoing HTLC. OutgoingCTLV uint32 + + // NextBlinding is an optional blinding point to be passed to the next + // node in UpdateAddHtlc. This field is set if the htlc is part of a + // blinded route. + NextBlinding lnwire.BlindingPointRecord } diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index 87691d192..68b08fe42 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -131,6 +131,10 @@ type BlindingProcessor interface { // ephemeral key provided. DecryptBlindedHopData(ephemPub *btcec.PublicKey, encryptedData []byte) ([]byte, error) + + // NextEphemeral returns the next hop's ephemeral key, calculated + // from the current ephemeral key provided. + NextEphemeral(*btcec.PublicKey) (*btcec.PublicKey, error) } // BlindingKit contains the components required to extract forwarding @@ -250,12 +254,40 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload, return nil, err } + // If we have an override for the blinding point for the next node, + // we'll just use it without tweaking (the sender intended to switch + // out directly for this blinding point). Otherwise, we'll tweak our + // blinding point to get the next ephemeral key. + nextEph, err := routeData.NextBlindingOverride.UnwrapOrFuncErr( + func() (tlv.RecordT[tlv.TlvType8, + *btcec.PublicKey], error) { + + next, err := b.Processor.NextEphemeral(blindingPoint) + if err != nil { + // Return a zero record because we expect the + // error to be checked. + return routeData.NextBlindingOverride.Zero(), + err + } + + return tlv.NewPrimitiveRecord[tlv.TlvType8](next), nil + }, + ) + if err != nil { + return nil, err + } + return &ForwardingInfo{ NextHop: routeData.ShortChannelID.Val, AmountToForward: fwdAmt, OutgoingCTLV: b.IncomingCltv - uint32( routeData.RelayInfo.Val.CltvExpiryDelta, ), + // Remap from blinding override type to blinding point type. + NextBlinding: tlv.SomeRecordT( + tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType]( + nextEph.Val), + ), }, nil } diff --git a/htlcswitch/hop/iterator_test.go b/htlcswitch/hop/iterator_test.go index e39dbb151..60919333b 100644 --- a/htlcswitch/hop/iterator_test.go +++ b/htlcswitch/hop/iterator_test.go @@ -170,6 +170,13 @@ func (m *mockProcessor) DecryptBlindedHopData(_ *btcec.PublicKey, return data, m.decryptErr } +// NextEphemeral mocks getting our next ephemeral key. +func (m *mockProcessor) NextEphemeral(*btcec.PublicKey) (*btcec.PublicKey, + error) { + + return nil, nil +} + // TestDecryptAndValidateFwdInfo tests deriving forwarding info using a // blinding kit. This test does not cover assertions on the calculations of // forwarding information, because this is covered in a test dedicated to those diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 3288584d5..45b9a8729 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3348,9 +3348,10 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // Otherwise, it was already processed, we can // can collect it and continue. addMsg := &lnwire.UpdateAddHTLC{ - Expiry: fwdInfo.OutgoingCTLV, - Amount: fwdInfo.AmountToForward, - PaymentHash: pd.RHash, + Expiry: fwdInfo.OutgoingCTLV, + Amount: fwdInfo.AmountToForward, + PaymentHash: pd.RHash, + BlindingPoint: fwdInfo.NextBlinding, } // Finally, we'll encode the onion packet for @@ -3393,9 +3394,10 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // create the outgoing HTLC using the parameters as // specified in the forwarding info. addMsg := &lnwire.UpdateAddHTLC{ - Expiry: fwdInfo.OutgoingCTLV, - Amount: fwdInfo.AmountToForward, - PaymentHash: pd.RHash, + Expiry: fwdInfo.OutgoingCTLV, + Amount: fwdInfo.AmountToForward, + PaymentHash: pd.RHash, + BlindingPoint: fwdInfo.NextBlinding, } // Finally, we'll encode the onion packet for the