Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,8 @@ func DefaultConfig() Config {
RejectCacheSize: channeldb.DefaultRejectCacheSize,
ChannelCacheSize: channeldb.DefaultChannelCacheSize,
},
Prometheus: lncfg.DefaultPrometheus(),
Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir),
ProtocolOptions: lncfg.DefaultProtocol(),
Prometheus: lncfg.DefaultPrometheus(),
Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir),
Comment thread
carlaKC marked this conversation as resolved.
HealthChecks: &lncfg.HealthCheckConfig{
ChainCheck: &lncfg.CheckConfig{
Interval: defaultChainInterval,
Expand Down
2 changes: 1 addition & 1 deletion contractcourt/htlc_incoming_contest_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
return nil, nil, err
}

payload, err := iterator.HopPayload()
payload, _, err := iterator.HopPayload()
if err != nil {
return nil, nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions contractcourt/htlc_incoming_contest_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ type mockHopIterator struct {
hop.Iterator
}

func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
func (h *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) {
var nextAddress [8]byte
if !h.isExit {
nextAddress = [8]byte{0x01}
Expand All @@ -275,7 +275,7 @@ func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
ForwardAmount: 100,
OutgoingCltv: 40,
ExtraBytes: [12]byte{},
}), nil
}), hop.RouteRoleCleartext, nil
}

func (h *mockHopIterator) EncodeNextHop(w io.Writer) error {
Expand Down
7 changes: 5 additions & 2 deletions docs/release-notes/release-notes-0.18.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,11 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor

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

* Introduced [fee bumper](https://github.com/lightningnetwork/lnd/pull/8424) to
handle bumping the fees of sweeping transactions properly. A
Expand Down
5 changes: 5 additions & 0 deletions feature/default_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ var defaultSetDesc = setDesc{
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.RouteBlindingOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
SetInvoice: {}, // 9
},
lnwire.ShutdownAnySegwitOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
Expand Down
6 changes: 6 additions & 0 deletions feature/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ var deps = depDesc{
lnwire.AnchorsZeroFeeHtlcTxOptional: {},
lnwire.ExplicitChannelTypeOptional: {},
},
lnwire.RouteBlindingOptional: {
lnwire.TLVOnionPayloadOptional: {},
},
lnwire.RouteBlindingRequired: {
lnwire.TLVOnionPayloadRequired: {},
},
}

// ValidateDeps asserts that a feature vector sets all features and their
Expand Down
10 changes: 9 additions & 1 deletion feature/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type Config struct {
// segwit witness versions for co-op closes.
NoAnySegwit bool

// NoRouteBlinding unsets route blinding feature bits.
NoRouteBlinding bool

// CustomFeatures is a set of custom features to advertise in each
// set.
CustomFeatures map[Set][]lnwire.FeatureBit
Expand Down Expand Up @@ -123,6 +126,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.PaymentAddrRequired)
raw.Unset(lnwire.MPPOptional)
raw.Unset(lnwire.MPPRequired)
raw.Unset(lnwire.RouteBlindingOptional)
raw.Unset(lnwire.RouteBlindingRequired)
Comment thread
carlaKC marked this conversation as resolved.
raw.Unset(lnwire.AMPOptional)
raw.Unset(lnwire.AMPRequired)
raw.Unset(lnwire.KeysendOptional)
Expand Down Expand Up @@ -179,7 +184,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.SimpleTaprootChannelsOptionalStaging)
raw.Unset(lnwire.SimpleTaprootChannelsRequiredStaging)
}

if cfg.NoRouteBlinding {
raw.Unset(lnwire.RouteBlindingOptional)
raw.Unset(lnwire.RouteBlindingRequired)
}
for _, custom := range cfg.CustomFeatures[set] {
if custom > set.Maximum() {
return nil, fmt.Errorf("feature bit: %v "+
Expand Down
6 changes: 6 additions & 0 deletions htlcswitch/circuit.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ func (c *PaymentCircuit) Decode(r io.Reader) error {
// Test encrypter.
c.ErrorEncrypter = NewMockObfuscator()

case hop.EncrypterTypeIntroduction:
c.ErrorEncrypter = hop.NewIntroductionErrorEncrypter()

case hop.EncrypterTypeRelaying:
c.ErrorEncrypter = hop.NewRelayingErrorEncrypter()

default:
return UnknownEncrypterType(encrypterType)
}
Expand Down
83 changes: 82 additions & 1 deletion htlcswitch/hop/error_encryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,26 @@ const (

// EncrypterTypeMock is used to identify a mock obfuscator instance.
EncrypterTypeMock = 2

// EncrypterTypeIntroduction is used to identify a sphinx onion error
// encrypter where we are the introduction node in a blinded route. It
// has the same functionality as EncrypterTypeSphinx, but is used to
// mark our special-case error handling.
EncrypterTypeIntroduction = 3

// EncrypterTypeRelaying is used to identify a sphinx onion error
// encryper where we are a relaying node in a blinded route. It has
// the same functionality as a EncrypterTypeSphinx, but is used to mark
// our special-case error handling.
EncrypterTypeRelaying = 4
)

// IsBlinded returns a boolean indicating whether the error encrypter belongs
// to a blinded route.
func (e EncrypterType) IsBlinded() bool {
return e == EncrypterTypeIntroduction || e == EncrypterTypeRelaying
}

// ErrorEncrypterExtracter defines a function signature that extracts an
// ErrorEncrypter from an sphinx OnionPacket.
type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter,
Expand Down Expand Up @@ -197,9 +215,72 @@ func (s *SphinxErrorEncrypter) Reextract(
s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter

return nil

}

// A compile time check to ensure SphinxErrorEncrypter implements the
// ErrorEncrypter interface.
var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil)

// A compile time check to ensure that IntroductionErrorEncrypter implements
// the ErrorEncrypter interface.
var _ ErrorEncrypter = (*IntroductionErrorEncrypter)(nil)

// IntroductionErrorEncrypter is a wrapper type on SphinxErrorEncrypter which
// is used to signal that we have special HTLC error handling for this hop.
type IntroductionErrorEncrypter struct {
// ErrorEncrypter is the underlying error encrypter, embedded
// directly in the struct so that we don't have to re-implement the
// ErrorEncrypter interface.
ErrorEncrypter
}

// NewIntroductionErrorEncrypter returns a blank IntroductionErrorEncrypter.
func NewIntroductionErrorEncrypter() *IntroductionErrorEncrypter {
return &IntroductionErrorEncrypter{
ErrorEncrypter: NewSphinxErrorEncrypter(),
}
}

// Type returns the identifier for an introduction error encrypter.
func (i *IntroductionErrorEncrypter) Type() EncrypterType {
return EncrypterTypeIntroduction
}

// Reextract rederives the error encrypter from the currently held EphemeralKey,
// relying on the logic in the underlying SphinxErrorEncrypter.
func (i *IntroductionErrorEncrypter) Reextract(
extract ErrorEncrypterExtracter) error {

return i.ErrorEncrypter.Reextract(extract)
}

// A compile time check to ensure that RelayingErrorEncrypte implements
// the ErrorEncrypter interface.
var _ ErrorEncrypter = (*RelayingErrorEncrypter)(nil)

// RelayingErrorEncrypter is a wrapper type on SphinxErrorEncrypter which
// is used to signal that we have special HTLC error handling for this hop.
type RelayingErrorEncrypter struct {
ErrorEncrypter
}

// NewRelayingErrorEncrypter returns a blank RelayingErrorEncrypter with
// an underlying SphinxErrorEncrypter.
func NewRelayingErrorEncrypter() *RelayingErrorEncrypter {
return &RelayingErrorEncrypter{
ErrorEncrypter: NewSphinxErrorEncrypter(),
}
}

// Type returns the identifier for a relaying error encrypter.
func (r *RelayingErrorEncrypter) Type() EncrypterType {
return EncrypterTypeRelaying
}

// Reextract rederives the error encrypter from the currently held EphemeralKey,
// relying on the logic in the underlying SphinxErrorEncrypter.
func (r *RelayingErrorEncrypter) Reextract(
extract ErrorEncrypterExtracter) error {

return r.ErrorEncrypter.Reextract(extract)
}
55 changes: 42 additions & 13 deletions htlcswitch/hop/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,37 +97,61 @@ func hopFromPayload(p *Payload) (*route.Hop, uint64) {

// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context
// that the hop should be final (which is usually obtained by the structure
// of the sphinx packet).
func FuzzPayloadFinal(f *testing.F) {
fuzzPayload(f, true)
// of the sphinx packet) for the case where a blinding point was provided in
// UpdateAddHtlc.
func FuzzPayloadFinalBlinding(f *testing.F) {
fuzzPayload(f, true, true)
}

// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context
// that the hop should be final (which is usually obtained by the structure
// of the sphinx packet) for the case where no blinding point was provided in
// UpdateAddHtlc.
func FuzzPayloadFinalNoBlinding(f *testing.F) {
fuzzPayload(f, true, false)
}

// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the
// additional context that a hop should be intermediate (which is usually
// obtained by the structure of the sphinx packet).
func FuzzPayloadIntermediate(f *testing.F) {
fuzzPayload(f, false)
// obtained by the structure of the sphinx packet) for the case where a
// blinding point was provided in UpdateAddHtlc.
func FuzzPayloadIntermediateBlinding(f *testing.F) {
fuzzPayload(f, false, true)
}

func fuzzPayload(f *testing.F, finalPayload bool) {
// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the
// additional context that a hop should be intermediate (which is usually
// obtained by the structure of the sphinx packet) for the case where no
// blinding point was provided in UpdateAddHtlc.
func FuzzPayloadIntermediateNoBlinding(f *testing.F) {
fuzzPayload(f, false, false)
}

func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) {
f.Fuzz(func(t *testing.T, data []byte) {
if len(data) > sphinx.MaxPayloadSize {
return
}

r := bytes.NewReader(data)

payload1, _, err := NewPayloadFromReader(r, finalPayload)
payload1, parsed, err := ParseTLVPayload(r)
if err != nil {
return
}

if err = ValidateParsedPayloadTypes(
parsed, finalPayload, updateAddBlinded,
); err != nil {
return
}

var b bytes.Buffer
hop, nextChanID := hopFromPayload(payload1)
err = hop.PackHopPayload(&b, nextChanID, finalPayload)
switch {
// PackHopPayload refuses to encode an AMP record
// without an MPP record. However, NewPayloadFromReader
// without an MPP record. However, ValidateParsedPayloadTypes
// does allow decoding an AMP record without an MPP
// record, since validation is done at a later stage. Do
// not report a bug for this case.
Expand All @@ -136,17 +160,22 @@ func fuzzPayload(f *testing.F, finalPayload bool) {

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

default:
require.NoError(t, err)
}

payload2, _, err := NewPayloadFromReader(&b, finalPayload)
payload2, parsed, err := ParseTLVPayload(&b)
require.NoError(t, err)

err = ValidateParsedPayloadTypes(
parsed, finalPayload, updateAddBlinded,
)
require.NoError(t, err)

require.Equal(t, payload1, payload2)
Expand Down
Loading