/ record / blinded_data.go
blinded_data.go
  1  package record
  2  
  3  import (
  4  	"bytes"
  5  	"encoding/binary"
  6  	"io"
  7  
  8  	"github.com/btcsuite/btcd/btcec/v2"
  9  	"github.com/lightningnetwork/lnd/fn/v2"
 10  	"github.com/lightningnetwork/lnd/lnwire"
 11  	"github.com/lightningnetwork/lnd/tlv"
 12  )
 13  
 14  // AverageDummyHopPayloadSize is the size of a standard blinded path dummy hop
 15  // payload. In most cases, this is larger than the other payload types and so
 16  // to make sure that a sender cannot use this fact to know if a dummy hop is
 17  // present or not, we'll make sure to always pad all payloads to at least this
 18  // size.
 19  const AverageDummyHopPayloadSize = 51
 20  
 21  // BlindedRouteData contains the information that is included in a blinded
 22  // route encrypted data blob that is created by the recipient to provide
 23  // forwarding information.
 24  type BlindedRouteData struct {
 25  	// Padding is an optional set of bytes that a recipient can use to pad
 26  	// the data so that the encrypted recipient data blobs are all the same
 27  	// length.
 28  	Padding tlv.OptionalRecordT[tlv.TlvType1, []byte]
 29  
 30  	// ShortChannelID is the channel ID of the next hop.
 31  	ShortChannelID tlv.OptionalRecordT[tlv.TlvType2, lnwire.ShortChannelID]
 32  
 33  	// NextNodeID is the node ID of the next node on the path. In the
 34  	// context of blinded path payments, this is used to indicate the
 35  	// presence of dummy hops that need to be peeled from the onion.
 36  	NextNodeID tlv.OptionalRecordT[tlv.TlvType4, *btcec.PublicKey]
 37  
 38  	// PathID is a secret set of bytes that the blinded path creator will
 39  	// set so that they can check the value on decryption to ensure that the
 40  	// path they created was used for the intended purpose.
 41  	PathID tlv.OptionalRecordT[tlv.TlvType6, []byte]
 42  
 43  	// NextBlindingOverride is a blinding point that should be switched
 44  	// in for the next hop. This is used to combine two blinded paths into
 45  	// one (which primarily is used in onion messaging, but in theory
 46  	// could be used for payments as well).
 47  	NextBlindingOverride tlv.OptionalRecordT[tlv.TlvType8, *btcec.PublicKey]
 48  
 49  	// RelayInfo provides the relay parameters for the hop.
 50  	RelayInfo tlv.OptionalRecordT[tlv.TlvType10, PaymentRelayInfo]
 51  
 52  	// Constraints provides the payment relay constraints for the hop.
 53  	Constraints tlv.OptionalRecordT[tlv.TlvType12, PaymentConstraints]
 54  
 55  	// Features is the set of features the payment requires.
 56  	Features tlv.OptionalRecordT[tlv.TlvType14, lnwire.FeatureVector]
 57  }
 58  
 59  // NewNonFinalBlindedRouteData creates the data that's provided for hops within
 60  // a blinded route.
 61  func NewNonFinalBlindedRouteData(chanID lnwire.ShortChannelID,
 62  	blindingOverride *btcec.PublicKey, relayInfo PaymentRelayInfo,
 63  	constraints *PaymentConstraints,
 64  	features *lnwire.FeatureVector) *BlindedRouteData {
 65  
 66  	info := &BlindedRouteData{
 67  		ShortChannelID: tlv.SomeRecordT(
 68  			tlv.NewRecordT[tlv.TlvType2](chanID),
 69  		),
 70  		RelayInfo: tlv.SomeRecordT(
 71  			tlv.NewRecordT[tlv.TlvType10](relayInfo),
 72  		),
 73  	}
 74  
 75  	if blindingOverride != nil {
 76  		info.NextBlindingOverride = tlv.SomeRecordT(
 77  			tlv.NewPrimitiveRecord[tlv.TlvType8](blindingOverride))
 78  	}
 79  
 80  	if constraints != nil {
 81  		info.Constraints = tlv.SomeRecordT(
 82  			tlv.NewRecordT[tlv.TlvType12](*constraints))
 83  	}
 84  
 85  	if features != nil {
 86  		info.Features = tlv.SomeRecordT(
 87  			tlv.NewRecordT[tlv.TlvType14](*features),
 88  		)
 89  	}
 90  
 91  	return info
 92  }
 93  
 94  // NewNonFinalBlindedRouteData creates the data that's provided for hops within
 95  // a blinded route.
 96  func NewNonFinalBlindedRouteDataOnionMessage(
 97  	nextNode fn.Either[*btcec.PublicKey, lnwire.ShortChannelID],
 98  	blindingOverride *btcec.PublicKey,
 99  	features *lnwire.FeatureVector) *BlindedRouteData {
100  
101  	info := fn.ElimEither(
102  		nextNode,
103  		func(nextNodeID *btcec.PublicKey) *BlindedRouteData {
104  			return &BlindedRouteData{
105  				NextNodeID: tlv.SomeRecordT(
106  					tlv.NewPrimitiveRecord[tlv.TlvType4](
107  						nextNodeID,
108  					),
109  				),
110  			}
111  		},
112  		func(chanID lnwire.ShortChannelID) *BlindedRouteData {
113  			return &BlindedRouteData{
114  				ShortChannelID: tlv.SomeRecordT(
115  					tlv.NewRecordT[tlv.TlvType2](chanID),
116  				),
117  			}
118  		},
119  	)
120  
121  	if blindingOverride != nil {
122  		info.NextBlindingOverride = tlv.SomeRecordT(
123  			tlv.NewPrimitiveRecord[tlv.TlvType8](blindingOverride))
124  	}
125  
126  	if features != nil {
127  		info.Features = tlv.SomeRecordT(
128  			tlv.NewRecordT[tlv.TlvType14](*features),
129  		)
130  	}
131  
132  	return info
133  }
134  
135  // NewFinalHopBlindedRouteData creates the data that's provided for the final
136  // hop in a blinded route.
137  func NewFinalHopBlindedRouteData(constraints *PaymentConstraints,
138  	pathID []byte) *BlindedRouteData {
139  
140  	var data BlindedRouteData
141  	if pathID != nil {
142  		data.PathID = tlv.SomeRecordT(
143  			tlv.NewPrimitiveRecord[tlv.TlvType6](pathID),
144  		)
145  	}
146  
147  	if constraints != nil {
148  		data.Constraints = tlv.SomeRecordT(
149  			tlv.NewRecordT[tlv.TlvType12](*constraints))
150  	}
151  
152  	return &data
153  }
154  
155  // NewDummyHopRouteData creates the data that's provided for any hop preceding
156  // a dummy hop. The presence of such a payload indicates to the reader that
157  // they are the intended recipient and should peel the remainder of the onion.
158  func NewDummyHopRouteData(ourPubKey *btcec.PublicKey,
159  	relayInfo PaymentRelayInfo,
160  	constraints PaymentConstraints) *BlindedRouteData {
161  
162  	return &BlindedRouteData{
163  		NextNodeID: tlv.SomeRecordT(
164  			tlv.NewPrimitiveRecord[tlv.TlvType4](ourPubKey),
165  		),
166  		RelayInfo: tlv.SomeRecordT(
167  			tlv.NewRecordT[tlv.TlvType10](relayInfo),
168  		),
169  		Constraints: tlv.SomeRecordT(
170  			tlv.NewRecordT[tlv.TlvType12](constraints),
171  		),
172  	}
173  }
174  
175  // DecodeBlindedRouteData decodes the data provided within a blinded route.
176  func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
177  	var (
178  		d BlindedRouteData
179  
180  		padding          = d.Padding.Zero()
181  		scid             = d.ShortChannelID.Zero()
182  		nextNodeID       = d.NextNodeID.Zero()
183  		pathID           = d.PathID.Zero()
184  		blindingOverride = d.NextBlindingOverride.Zero()
185  		relayInfo        = d.RelayInfo.Zero()
186  		constraints      = d.Constraints.Zero()
187  		features         = d.Features.Zero()
188  	)
189  
190  	var tlvRecords lnwire.ExtraOpaqueData
191  	if err := lnwire.ReadElements(r, &tlvRecords); err != nil {
192  		return nil, err
193  	}
194  
195  	typeMap, err := tlvRecords.ExtractRecords(
196  		&padding, &scid, &nextNodeID, &pathID, &blindingOverride,
197  		&relayInfo, &constraints, &features,
198  	)
199  	if err != nil {
200  		return nil, err
201  	}
202  
203  	val, ok := typeMap[d.Padding.TlvType()]
204  	if ok && val == nil {
205  		d.Padding = tlv.SomeRecordT(padding)
206  	}
207  
208  	if val, ok := typeMap[d.ShortChannelID.TlvType()]; ok && val == nil {
209  		d.ShortChannelID = tlv.SomeRecordT(scid)
210  	}
211  
212  	if val, ok := typeMap[d.NextNodeID.TlvType()]; ok && val == nil {
213  		d.NextNodeID = tlv.SomeRecordT(nextNodeID)
214  	}
215  
216  	if val, ok := typeMap[d.PathID.TlvType()]; ok && val == nil {
217  		d.PathID = tlv.SomeRecordT(pathID)
218  	}
219  
220  	val, ok = typeMap[d.NextBlindingOverride.TlvType()]
221  	if ok && val == nil {
222  		d.NextBlindingOverride = tlv.SomeRecordT(blindingOverride)
223  	}
224  
225  	if val, ok := typeMap[d.RelayInfo.TlvType()]; ok && val == nil {
226  		d.RelayInfo = tlv.SomeRecordT(relayInfo)
227  	}
228  
229  	if val, ok := typeMap[d.Constraints.TlvType()]; ok && val == nil {
230  		d.Constraints = tlv.SomeRecordT(constraints)
231  	}
232  
233  	if val, ok := typeMap[d.Features.TlvType()]; ok && val == nil {
234  		d.Features = tlv.SomeRecordT(features)
235  	}
236  
237  	return &d, nil
238  }
239  
240  // EncodeBlindedRouteData encodes the blinded route data provided.
241  func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
242  	var (
243  		e               lnwire.ExtraOpaqueData
244  		recordProducers = make([]tlv.RecordProducer, 0, 5)
245  	)
246  
247  	data.Padding.WhenSome(func(p tlv.RecordT[tlv.TlvType1, []byte]) {
248  		recordProducers = append(recordProducers, &p)
249  	})
250  
251  	data.ShortChannelID.WhenSome(func(scid tlv.RecordT[tlv.TlvType2,
252  		lnwire.ShortChannelID]) {
253  
254  		recordProducers = append(recordProducers, &scid)
255  	})
256  
257  	data.NextNodeID.WhenSome(func(f tlv.RecordT[tlv.TlvType4,
258  		*btcec.PublicKey]) {
259  
260  		recordProducers = append(recordProducers, &f)
261  	})
262  
263  	data.PathID.WhenSome(func(pathID tlv.RecordT[tlv.TlvType6, []byte]) {
264  		recordProducers = append(recordProducers, &pathID)
265  	})
266  
267  	data.NextBlindingOverride.WhenSome(func(pk tlv.RecordT[tlv.TlvType8,
268  		*btcec.PublicKey]) {
269  
270  		recordProducers = append(recordProducers, &pk)
271  	})
272  
273  	data.RelayInfo.WhenSome(func(r tlv.RecordT[tlv.TlvType10,
274  		PaymentRelayInfo]) {
275  
276  		recordProducers = append(recordProducers, &r)
277  	})
278  
279  	data.Constraints.WhenSome(func(cs tlv.RecordT[tlv.TlvType12,
280  		PaymentConstraints]) {
281  
282  		recordProducers = append(recordProducers, &cs)
283  	})
284  
285  	data.Features.WhenSome(func(f tlv.RecordT[tlv.TlvType14,
286  		lnwire.FeatureVector]) {
287  
288  		recordProducers = append(recordProducers, &f)
289  	})
290  
291  	if err := e.PackRecords(recordProducers...); err != nil {
292  		return nil, err
293  	}
294  
295  	return e[:], nil
296  }
297  
298  // PadBy adds "n" padding bytes to the BlindedRouteData using the Padding field.
299  // Callers should be aware that the total payload size will change by more than
300  // "n" since the "n" bytes will be prefixed by BigSize type and length fields.
301  // Callers may need to call PadBy iteratively until each encrypted data packet
302  // is the same size and so each call will overwrite the Padding record.
303  // Note that calling PadBy with an n value of 0 will still result in a zero
304  // length TLV entry being added.
305  func (b *BlindedRouteData) PadBy(n int) {
306  	b.Padding = tlv.SomeRecordT(
307  		tlv.NewPrimitiveRecord[tlv.TlvType1](make([]byte, n)),
308  	)
309  }
310  
311  // PaymentRelayInfo describes the relay policy for a blinded path.
312  type PaymentRelayInfo struct {
313  	// CltvExpiryDelta is the expiry delta for the payment.
314  	CltvExpiryDelta uint16
315  
316  	// FeeRate is the fee rate that will be charged per millionth of a
317  	// satoshi.
318  	FeeRate uint32
319  
320  	// BaseFee is the per-htlc fee charged in milli-satoshis.
321  	BaseFee lnwire.MilliSatoshi
322  }
323  
324  // Record creates a tlv.Record that encodes the payment relay (type 10) type for
325  // an encrypted blob payload.
326  func (i *PaymentRelayInfo) Record() tlv.Record {
327  	return tlv.MakeDynamicRecord(
328  		10, &i, func() uint64 {
329  			// uint16 + uint32 + tuint32
330  			return 2 + 4 + tlv.SizeTUint32(uint32(i.BaseFee))
331  		}, encodePaymentRelay, decodePaymentRelay,
332  	)
333  }
334  
335  func encodePaymentRelay(w io.Writer, val interface{}, buf *[8]byte) error {
336  	if t, ok := val.(**PaymentRelayInfo); ok {
337  		relayInfo := *t
338  
339  		// Just write our first 6 bytes directly.
340  		binary.BigEndian.PutUint16(buf[:2], relayInfo.CltvExpiryDelta)
341  		binary.BigEndian.PutUint32(buf[2:6], relayInfo.FeeRate)
342  		if _, err := w.Write(buf[0:6]); err != nil {
343  			return err
344  		}
345  
346  		baseFee := uint32(relayInfo.BaseFee)
347  
348  		// We can safely reuse buf here because we overwrite its
349  		// contents.
350  		return tlv.ETUint32(w, &baseFee, buf)
351  	}
352  
353  	return tlv.NewTypeForEncodingErr(val, "**hop.PaymentRelayInfo")
354  }
355  
356  func decodePaymentRelay(r io.Reader, val interface{}, buf *[8]byte,
357  	l uint64) error {
358  
359  	if t, ok := val.(**PaymentRelayInfo); ok && l <= 10 {
360  		scratch := make([]byte, l)
361  
362  		n, err := io.ReadFull(r, scratch)
363  		if err != nil {
364  			return err
365  		}
366  
367  		// We expect at least 6 bytes, because we have 2 bytes for
368  		// cltv delta and 4 bytes for fee rate.
369  		if n < 6 {
370  			return tlv.NewTypeForDecodingErr(val,
371  				"*hop.paymentRelayInfo", uint64(n), 6)
372  		}
373  
374  		relayInfo := *t
375  
376  		relayInfo.CltvExpiryDelta = binary.BigEndian.Uint16(
377  			scratch[0:2],
378  		)
379  		relayInfo.FeeRate = binary.BigEndian.Uint32(scratch[2:6])
380  
381  		// To be able to re-use the DTUint32 function we create a
382  		// buffer with just the bytes holding the variable length u32.
383  		// If the base fee is zero, this will be an empty buffer, which
384  		// is okay.
385  		b := bytes.NewBuffer(scratch[6:])
386  
387  		var baseFee uint32
388  		err = tlv.DTUint32(b, &baseFee, buf, l-6)
389  		if err != nil {
390  			return err
391  		}
392  
393  		relayInfo.BaseFee = lnwire.MilliSatoshi(baseFee)
394  
395  		return nil
396  	}
397  
398  	return tlv.NewTypeForDecodingErr(val, "*hop.paymentRelayInfo", l, 10)
399  }
400  
401  // PaymentConstraints is a set of restrictions on a payment.
402  type PaymentConstraints struct {
403  	// MaxCltvExpiry is the maximum expiry height for the payment.
404  	MaxCltvExpiry uint32
405  
406  	// HtlcMinimumMsat is the minimum htlc size for the payment.
407  	HtlcMinimumMsat lnwire.MilliSatoshi
408  }
409  
410  func (p *PaymentConstraints) Record() tlv.Record {
411  	return tlv.MakeDynamicRecord(
412  		12, &p, func() uint64 {
413  			// uint32 + tuint64.
414  			return 4 + tlv.SizeTUint64(uint64(
415  				p.HtlcMinimumMsat,
416  			))
417  		},
418  		encodePaymentConstraints, decodePaymentConstraints,
419  	)
420  }
421  
422  func encodePaymentConstraints(w io.Writer, val interface{},
423  	buf *[8]byte) error {
424  
425  	if c, ok := val.(**PaymentConstraints); ok {
426  		constraints := *c
427  
428  		binary.BigEndian.PutUint32(buf[:4], constraints.MaxCltvExpiry)
429  		if _, err := w.Write(buf[:4]); err != nil {
430  			return err
431  		}
432  
433  		// We can safely re-use buf here because we overwrite its
434  		// contents.
435  		htlcMsat := uint64(constraints.HtlcMinimumMsat)
436  
437  		return tlv.ETUint64(w, &htlcMsat, buf)
438  	}
439  
440  	return tlv.NewTypeForEncodingErr(val, "**PaymentConstraints")
441  }
442  
443  func decodePaymentConstraints(r io.Reader, val interface{}, buf *[8]byte,
444  	l uint64) error {
445  
446  	if c, ok := val.(**PaymentConstraints); ok && l <= 12 {
447  		scratch := make([]byte, l)
448  
449  		n, err := io.ReadFull(r, scratch)
450  		if err != nil {
451  			return err
452  		}
453  
454  		// We expect at least 4 bytes for our uint32.
455  		if n < 4 {
456  			return tlv.NewTypeForDecodingErr(val,
457  				"*paymentConstraints", uint64(n), 4)
458  		}
459  
460  		payConstraints := *c
461  
462  		payConstraints.MaxCltvExpiry = binary.BigEndian.Uint32(
463  			scratch[:4],
464  		)
465  
466  		// This could be empty if our minimum is zero, that's okay.
467  		var (
468  			b       = bytes.NewBuffer(scratch[4:])
469  			minHtlc uint64
470  		)
471  
472  		err = tlv.DTUint64(b, &minHtlc, buf, l-4)
473  		if err != nil {
474  			return err
475  		}
476  		payConstraints.HtlcMinimumMsat = lnwire.MilliSatoshi(minHtlc)
477  
478  		return nil
479  	}
480  
481  	return tlv.NewTypeForDecodingErr(val, "**PaymentConstraints", l, l)
482  }