Skip to content

Commit add2691

Browse files
authored
Merge pull request #8485 from carlaKC/7298-3-forwardblindedroutes
[3/3]: Blinded Route Error Handling
2 parents e6d6789 + 6572f5c commit add2691

29 files changed

+1324
-523
lines changed

config.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,8 @@ func DefaultConfig() Config {
627627
RejectCacheSize: channeldb.DefaultRejectCacheSize,
628628
ChannelCacheSize: channeldb.DefaultChannelCacheSize,
629629
},
630-
Prometheus: lncfg.DefaultPrometheus(),
631-
Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir),
632-
ProtocolOptions: lncfg.DefaultProtocol(),
630+
Prometheus: lncfg.DefaultPrometheus(),
631+
Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir),
633632
HealthChecks: &lncfg.HealthCheckConfig{
634633
ChainCheck: &lncfg.CheckConfig{
635634
Interval: defaultChainInterval,

contractcourt/htlc_incoming_contest_resolver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
543543
return nil, nil, err
544544
}
545545

546-
payload, err := iterator.HopPayload()
546+
payload, _, err := iterator.HopPayload()
547547
if err != nil {
548548
return nil, nil, err
549549
}

contractcourt/htlc_incoming_contest_resolver_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ type mockHopIterator struct {
263263
hop.Iterator
264264
}
265265

266-
func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
266+
func (h *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) {
267267
var nextAddress [8]byte
268268
if !h.isExit {
269269
nextAddress = [8]byte{0x01}
@@ -275,7 +275,7 @@ func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
275275
ForwardAmount: 100,
276276
OutgoingCltv: 40,
277277
ExtraBytes: [12]byte{},
278-
}), nil
278+
}), hop.RouteRoleCleartext, nil
279279
}
280280

281281
func (h *mockHopIterator) EncodeNextHop(w io.Writer) error {

docs/release-notes/release-notes-0.18.0.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,11 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor
215215

216216
* [Preparatory work](https://github.com/lightningnetwork/lnd/pull/8159) for
217217
forwarding of blinded routes was added, along with [support](https://github.com/lightningnetwork/lnd/pull/8160)
218-
for forwarding blinded payments. Forwarding of blinded payments is disabled
219-
by default, and the feature is not yet advertised to the network.
218+
for forwarding blinded payments and [error handling](https://github.com/lightningnetwork/lnd/pull/8485).
219+
With this change, LND is now eligible to be selected as part of a blinded
220+
route and can forward payments on behalf of nodes that have support for
221+
receiving to blinded paths. This upgrade provides a meaningful improvement
222+
to the anonymity set and usability of blinded paths in the Lightning Network.
220223

221224
* Introduced [fee bumper](https://github.com/lightningnetwork/lnd/pull/8424) to
222225
handle bumping the fees of sweeping transactions properly. A

feature/default_sets.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ var defaultSetDesc = setDesc{
7979
SetInit: {}, // I
8080
SetNodeAnn: {}, // N
8181
},
82+
lnwire.RouteBlindingOptional: {
83+
SetInit: {}, // I
84+
SetNodeAnn: {}, // N
85+
SetInvoice: {}, // 9
86+
},
8287
lnwire.ShutdownAnySegwitOptional: {
8388
SetInit: {}, // I
8489
SetNodeAnn: {}, // N

feature/deps.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ var deps = depDesc{
7979
lnwire.AnchorsZeroFeeHtlcTxOptional: {},
8080
lnwire.ExplicitChannelTypeOptional: {},
8181
},
82+
lnwire.RouteBlindingOptional: {
83+
lnwire.TLVOnionPayloadOptional: {},
84+
},
85+
lnwire.RouteBlindingRequired: {
86+
lnwire.TLVOnionPayloadRequired: {},
87+
},
8288
}
8389

8490
// ValidateDeps asserts that a feature vector sets all features and their

feature/manager.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ type Config struct {
6060
// segwit witness versions for co-op closes.
6161
NoAnySegwit bool
6262

63+
// NoRouteBlinding unsets route blinding feature bits.
64+
NoRouteBlinding bool
65+
6366
// CustomFeatures is a set of custom features to advertise in each
6467
// set.
6568
CustomFeatures map[Set][]lnwire.FeatureBit
@@ -123,6 +126,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
123126
raw.Unset(lnwire.PaymentAddrRequired)
124127
raw.Unset(lnwire.MPPOptional)
125128
raw.Unset(lnwire.MPPRequired)
129+
raw.Unset(lnwire.RouteBlindingOptional)
130+
raw.Unset(lnwire.RouteBlindingRequired)
126131
raw.Unset(lnwire.AMPOptional)
127132
raw.Unset(lnwire.AMPRequired)
128133
raw.Unset(lnwire.KeysendOptional)
@@ -179,7 +184,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
179184
raw.Unset(lnwire.SimpleTaprootChannelsOptionalStaging)
180185
raw.Unset(lnwire.SimpleTaprootChannelsRequiredStaging)
181186
}
182-
187+
if cfg.NoRouteBlinding {
188+
raw.Unset(lnwire.RouteBlindingOptional)
189+
raw.Unset(lnwire.RouteBlindingRequired)
190+
}
183191
for _, custom := range cfg.CustomFeatures[set] {
184192
if custom > set.Maximum() {
185193
return nil, fmt.Errorf("feature bit: %v "+

htlcswitch/circuit.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ func (c *PaymentCircuit) Decode(r io.Reader) error {
205205
// Test encrypter.
206206
c.ErrorEncrypter = NewMockObfuscator()
207207

208+
case hop.EncrypterTypeIntroduction:
209+
c.ErrorEncrypter = hop.NewIntroductionErrorEncrypter()
210+
211+
case hop.EncrypterTypeRelaying:
212+
c.ErrorEncrypter = hop.NewRelayingErrorEncrypter()
213+
208214
default:
209215
return UnknownEncrypterType(encrypterType)
210216
}

htlcswitch/hop/error_encryptor.go

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,26 @@ const (
2525

2626
// EncrypterTypeMock is used to identify a mock obfuscator instance.
2727
EncrypterTypeMock = 2
28+
29+
// EncrypterTypeIntroduction is used to identify a sphinx onion error
30+
// encrypter where we are the introduction node in a blinded route. It
31+
// has the same functionality as EncrypterTypeSphinx, but is used to
32+
// mark our special-case error handling.
33+
EncrypterTypeIntroduction = 3
34+
35+
// EncrypterTypeRelaying is used to identify a sphinx onion error
36+
// encryper where we are a relaying node in a blinded route. It has
37+
// the same functionality as a EncrypterTypeSphinx, but is used to mark
38+
// our special-case error handling.
39+
EncrypterTypeRelaying = 4
2840
)
2941

42+
// IsBlinded returns a boolean indicating whether the error encrypter belongs
43+
// to a blinded route.
44+
func (e EncrypterType) IsBlinded() bool {
45+
return e == EncrypterTypeIntroduction || e == EncrypterTypeRelaying
46+
}
47+
3048
// ErrorEncrypterExtracter defines a function signature that extracts an
3149
// ErrorEncrypter from an sphinx OnionPacket.
3250
type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter,
@@ -197,9 +215,72 @@ func (s *SphinxErrorEncrypter) Reextract(
197215
s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter
198216

199217
return nil
200-
201218
}
202219

203220
// A compile time check to ensure SphinxErrorEncrypter implements the
204221
// ErrorEncrypter interface.
205222
var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil)
223+
224+
// A compile time check to ensure that IntroductionErrorEncrypter implements
225+
// the ErrorEncrypter interface.
226+
var _ ErrorEncrypter = (*IntroductionErrorEncrypter)(nil)
227+
228+
// IntroductionErrorEncrypter is a wrapper type on SphinxErrorEncrypter which
229+
// is used to signal that we have special HTLC error handling for this hop.
230+
type IntroductionErrorEncrypter struct {
231+
// ErrorEncrypter is the underlying error encrypter, embedded
232+
// directly in the struct so that we don't have to re-implement the
233+
// ErrorEncrypter interface.
234+
ErrorEncrypter
235+
}
236+
237+
// NewIntroductionErrorEncrypter returns a blank IntroductionErrorEncrypter.
238+
func NewIntroductionErrorEncrypter() *IntroductionErrorEncrypter {
239+
return &IntroductionErrorEncrypter{
240+
ErrorEncrypter: NewSphinxErrorEncrypter(),
241+
}
242+
}
243+
244+
// Type returns the identifier for an introduction error encrypter.
245+
func (i *IntroductionErrorEncrypter) Type() EncrypterType {
246+
return EncrypterTypeIntroduction
247+
}
248+
249+
// Reextract rederives the error encrypter from the currently held EphemeralKey,
250+
// relying on the logic in the underlying SphinxErrorEncrypter.
251+
func (i *IntroductionErrorEncrypter) Reextract(
252+
extract ErrorEncrypterExtracter) error {
253+
254+
return i.ErrorEncrypter.Reextract(extract)
255+
}
256+
257+
// A compile time check to ensure that RelayingErrorEncrypte implements
258+
// the ErrorEncrypter interface.
259+
var _ ErrorEncrypter = (*RelayingErrorEncrypter)(nil)
260+
261+
// RelayingErrorEncrypter is a wrapper type on SphinxErrorEncrypter which
262+
// is used to signal that we have special HTLC error handling for this hop.
263+
type RelayingErrorEncrypter struct {
264+
ErrorEncrypter
265+
}
266+
267+
// NewRelayingErrorEncrypter returns a blank RelayingErrorEncrypter with
268+
// an underlying SphinxErrorEncrypter.
269+
func NewRelayingErrorEncrypter() *RelayingErrorEncrypter {
270+
return &RelayingErrorEncrypter{
271+
ErrorEncrypter: NewSphinxErrorEncrypter(),
272+
}
273+
}
274+
275+
// Type returns the identifier for a relaying error encrypter.
276+
func (r *RelayingErrorEncrypter) Type() EncrypterType {
277+
return EncrypterTypeRelaying
278+
}
279+
280+
// Reextract rederives the error encrypter from the currently held EphemeralKey,
281+
// relying on the logic in the underlying SphinxErrorEncrypter.
282+
func (r *RelayingErrorEncrypter) Reextract(
283+
extract ErrorEncrypterExtracter) error {
284+
285+
return r.ErrorEncrypter.Reextract(extract)
286+
}

htlcswitch/hop/fuzz_test.go

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,37 +97,61 @@ func hopFromPayload(p *Payload) (*route.Hop, uint64) {
9797

9898
// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context
9999
// that the hop should be final (which is usually obtained by the structure
100-
// of the sphinx packet).
101-
func FuzzPayloadFinal(f *testing.F) {
102-
fuzzPayload(f, true)
100+
// of the sphinx packet) for the case where a blinding point was provided in
101+
// UpdateAddHtlc.
102+
func FuzzPayloadFinalBlinding(f *testing.F) {
103+
fuzzPayload(f, true, true)
104+
}
105+
106+
// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context
107+
// that the hop should be final (which is usually obtained by the structure
108+
// of the sphinx packet) for the case where no blinding point was provided in
109+
// UpdateAddHtlc.
110+
func FuzzPayloadFinalNoBlinding(f *testing.F) {
111+
fuzzPayload(f, true, false)
103112
}
104113

105114
// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the
106115
// additional context that a hop should be intermediate (which is usually
107-
// obtained by the structure of the sphinx packet).
108-
func FuzzPayloadIntermediate(f *testing.F) {
109-
fuzzPayload(f, false)
116+
// obtained by the structure of the sphinx packet) for the case where a
117+
// blinding point was provided in UpdateAddHtlc.
118+
func FuzzPayloadIntermediateBlinding(f *testing.F) {
119+
fuzzPayload(f, false, true)
110120
}
111121

112-
func fuzzPayload(f *testing.F, finalPayload bool) {
122+
// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the
123+
// additional context that a hop should be intermediate (which is usually
124+
// obtained by the structure of the sphinx packet) for the case where no
125+
// blinding point was provided in UpdateAddHtlc.
126+
func FuzzPayloadIntermediateNoBlinding(f *testing.F) {
127+
fuzzPayload(f, false, false)
128+
}
129+
130+
func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) {
113131
f.Fuzz(func(t *testing.T, data []byte) {
114132
if len(data) > sphinx.MaxPayloadSize {
115133
return
116134
}
117135

118136
r := bytes.NewReader(data)
119137

120-
payload1, _, err := NewPayloadFromReader(r, finalPayload)
138+
payload1, parsed, err := ParseTLVPayload(r)
121139
if err != nil {
122140
return
123141
}
124142

143+
if err = ValidateParsedPayloadTypes(
144+
parsed, finalPayload, updateAddBlinded,
145+
); err != nil {
146+
return
147+
}
148+
125149
var b bytes.Buffer
126150
hop, nextChanID := hopFromPayload(payload1)
127151
err = hop.PackHopPayload(&b, nextChanID, finalPayload)
128152
switch {
129153
// PackHopPayload refuses to encode an AMP record
130-
// without an MPP record. However, NewPayloadFromReader
154+
// without an MPP record. However, ValidateParsedPayloadTypes
131155
// does allow decoding an AMP record without an MPP
132156
// record, since validation is done at a later stage. Do
133157
// not report a bug for this case.
@@ -136,17 +160,22 @@ func fuzzPayload(f *testing.F, finalPayload bool) {
136160

137161
// PackHopPayload will not encode regular payloads or final
138162
// hops in blinded routes that do not have an amount or expiry
139-
// TLV set. However, NewPayloadFromReader will allow creation
140-
// of payloads where these TLVs are present, but they have
141-
// zero values because validation is done at a later stage.
163+
// TLV set. However, ValidateParsedPayloadTypes will allow
164+
// creation of payloads where these TLVs are present, but they
165+
// have zero values because validation is done at a later stage.
142166
case errors.Is(err, route.ErrMissingField):
143167
return
144168

145169
default:
146170
require.NoError(t, err)
147171
}
148172

149-
payload2, _, err := NewPayloadFromReader(&b, finalPayload)
173+
payload2, parsed, err := ParseTLVPayload(&b)
174+
require.NoError(t, err)
175+
176+
err = ValidateParsedPayloadTypes(
177+
parsed, finalPayload, updateAddBlinded,
178+
)
150179
require.NoError(t, err)
151180

152181
require.Equal(t, payload1, payload2)

0 commit comments

Comments
 (0)