Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Temp 7298 #43

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c2c0158
htlcswitch: handle malformed HTLC with invalid onion blinding code
carlaKC Mar 8, 2024
4d051b4
multi: handle all blinding point validation in ValidateParsedPayloadT…
carlaKC Apr 22, 2024
b81a6f3
htlcswitch: split parsing and validation of TLV payloads
carlaKC Apr 23, 2024
776c889
multi: return route role from HopPayload
carlaKC Apr 25, 2024
9f038c6
htlcswitch: introduce wrapper type error encrypter to identify blinded
carlaKC Apr 8, 2024
72260ad
htlcswitch: create error obfuscator with wrapped type for blinded
carlaKC Apr 23, 2024
de9c9c0
htlcswitch: set packet obfuscator for failures through switch
carlaKC Apr 8, 2024
4368718
htlcswitch: convert blinded failures for blinded payments
carlaKC Apr 8, 2024
d13a73a
itest: add test coverage for failure at blinded receiver
carlaKC Feb 15, 2024
4535cf6
itest: add coverage for failure within a blinded route
carlaKC Feb 15, 2024
428a33f
itest: add coverage for failure at the introduction node
carlaKC Feb 15, 2024
d57c6fa
itest: add coverage for disabling blinded forwards
carlaKC Mar 15, 2024
2140f19
itest: manually set timeout on cancel payment and provide cancel
carlaKC Apr 25, 2024
013d619
itest: add coverage for blinded error resolution from on-chain failure
carlaKC Apr 8, 2024
f635fa3
multi: enable optional route blinding feature
carlaKC Feb 17, 2024
a35e5fc
multi: turn on route blinding by default
carlaKC Mar 22, 2024
e826d5e
docs: add error handling link
carlaKC Feb 21, 2024
b53617b
fixup! itest: add coverage for blinded error resolution from on-chain…
carlaKC Apr 26, 2024
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),
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)
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
Loading