• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 16673052227

01 Aug 2025 10:44AM UTC coverage: 67.016% (-0.03%) from 67.047%
16673052227

Pull #9888

github

web-flow
Merge 1dd8765d7 into 37523b6cb
Pull Request #9888: Attributable failures

325 of 384 new or added lines in 16 files covered. (84.64%)

131 existing lines in 24 files now uncovered.

135611 of 202355 relevant lines covered (67.02%)

21613.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/htlcswitch/failure.go
1
package htlcswitch
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "strings"
7

8
        sphinx "github.com/lightningnetwork/lightning-onion"
9
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
// ClearTextError is an interface which is implemented by errors that occur
14
// when we know the underlying wire failure message. These errors are the
15
// opposite to opaque errors which are onion-encrypted blobs only understandable
16
// to the initiating node. ClearTextErrors are used when we fail a htlc at our
17
// node, or one of our initiated payments failed and we can decrypt the onion
18
// encrypted error fully.
19
type ClearTextError interface {
20
        error
21

22
        // WireMessage extracts a valid wire failure message from an internal
23
        // error which may contain additional metadata (which should not be
24
        // exposed to the network). This value may be nil in the case where
25
        // an unknown wire error is returned by one of our peers.
26
        WireMessage() lnwire.FailureMessage
27
}
28

29
// LinkError is an implementation of the ClearTextError interface which
30
// represents failures that occur on our incoming or outgoing link.
31
type LinkError struct {
32
        // msg returns the wire failure associated with the error.
33
        // This value should *not* be nil, because we should always
34
        // know the failure type for failures which occur at our own
35
        // node.
36
        msg lnwire.FailureMessage
37

38
        // FailureDetail enriches the wire error with additional information.
39
        FailureDetail
40
}
41

42
// NewLinkError returns a LinkError with the failure message provided.
43
// The failure message provided should *not* be nil, because we should
44
// always know the failure type for failures which occur at our own node.
45
func NewLinkError(msg lnwire.FailureMessage) *LinkError {
135✔
46
        return &LinkError{msg: msg}
135✔
47
}
135✔
48

49
// NewDetailedLinkError returns a link error that enriches a wire message with
50
// a failure detail.
51
func NewDetailedLinkError(msg lnwire.FailureMessage,
52
        detail FailureDetail) *LinkError {
72✔
53

72✔
54
        return &LinkError{
72✔
55
                msg:           msg,
72✔
56
                FailureDetail: detail,
72✔
57
        }
72✔
58
}
72✔
59

60
// WireMessage extracts a valid wire failure message from an internal
61
// error which may contain additional metadata (which should not be
62
// exposed to the network). This value should never be nil for LinkErrors,
63
// because we are the ones failing the htlc.
64
//
65
// Note this is part of the ClearTextError interface.
66
func (l *LinkError) WireMessage() lnwire.FailureMessage {
148✔
67
        return l.msg
148✔
68
}
148✔
69

70
// Error returns the string representation of a link error.
71
//
72
// Note this is part of the ClearTextError interface.
73
func (l *LinkError) Error() string {
29✔
74
        // If the link error has no failure detail, return the wire message's
29✔
75
        // error.
29✔
76
        if l.FailureDetail == nil {
49✔
77
                return l.msg.Error()
20✔
78
        }
20✔
79

80
        return l.FailureDetail.FailureString()
12✔
81
}
82

83
// ForwardingError wraps an lnwire.FailureMessage in a struct that also
84
// includes the source of the error.
85
type ForwardingError struct {
86
        // FailureSourceIdx is the index of the node that sent the failure. With
87
        // this information, the dispatcher of a payment can modify their set of
88
        // candidate routes in response to the type of failure extracted. Index
89
        // zero is the self node.
90
        FailureSourceIdx int
91

92
        // msg is the wire message associated with the error. This value may
93
        // be nil in the case where we fail to decode failure message sent by
94
        // a peer.
95
        msg lnwire.FailureMessage
96

97
        // HoldTimes is an array of hold times (in ms) as reported from the
98
        // nodes of the route. It is the time for which a node held the HTLC for
99
        // from that nodes local perspective. The first element corresponds to
100
        // the first node after the sender node, with greater indices indicating
101
        // nodes further down the route.
102
        HoldTimes []uint32
103
}
104

105
// WireMessage extracts a valid wire failure message from an internal
106
// error which may contain additional metadata (which should not be
107
// exposed to the network). This value may be nil in the case where
108
// an unknown wire error is returned by one of our peers.
109
//
110
// Note this is part of the ClearTextError interface.
111
func (f *ForwardingError) WireMessage() lnwire.FailureMessage {
161✔
112
        return f.msg
161✔
113
}
161✔
114

115
// Error implements the built-in error interface. We use this method to allow
116
// the switch or any callers to insert additional context to the error message
117
// returned.
118
func (f *ForwardingError) Error() string {
3✔
119
        return fmt.Sprintf(
3✔
120
                "%v@%v", f.msg, f.FailureSourceIdx,
3✔
121
        )
3✔
122
}
3✔
123

124
// NewForwardingError creates a new payment error which wraps a wire error
125
// with additional metadata.
126
func NewForwardingError(failure lnwire.FailureMessage,
127
        index int, holdTimes []uint32) *ForwardingError {
141✔
128

141✔
129
        return &ForwardingError{
141✔
130
                FailureSourceIdx: index,
141✔
131
                msg:              failure,
141✔
132
                HoldTimes:        holdTimes,
141✔
133
        }
141✔
134
}
141✔
135

136
// NewUnknownForwardingError returns a forwarding error which has a nil failure
137
// message. This constructor should only be used in the case where we cannot
138
// decode the failure we have received from a peer.
139
func NewUnknownForwardingError(index int) *ForwardingError {
1✔
140
        return &ForwardingError{
1✔
141
                FailureSourceIdx: index,
1✔
142
        }
1✔
143
}
1✔
144

145
// ErrorDecrypter is an interface that is used to decrypt the onion encrypted
146
// failure reason an extra out a well formed error.
147
type ErrorDecrypter interface {
148
        // DecryptError peels off each layer of onion encryption from the first
149
        // hop, to the source of the error. A fully populated
150
        // lnwire.FailureMessage is returned along with the source of the
151
        // error.
152
        DecryptError(lnwire.OpaqueReason, []byte) (*ForwardingError, error)
153
}
154

155
// UnknownEncrypterType is an error message used to signal that an unexpected
156
// EncrypterType was encountered during decoding.
157
type UnknownEncrypterType hop.EncrypterType
158

159
// Error returns a formatted error indicating the invalid EncrypterType.
160
func (e UnknownEncrypterType) Error() string {
×
161
        return fmt.Sprintf("unknown error encrypter type: %d", e)
×
162
}
×
163

164
// OnionErrorDecrypter is the interface that provides onion level error
165
// decryption.
166
type OnionErrorDecrypter interface {
167
        // DecryptError attempts to decrypt the passed encrypted error response.
168
        // The onion failure is encrypted in backward manner, starting from the
169
        // node where error have occurred. As a result, in order to decrypt the
170
        // error we need get all shared secret and apply decryption in the
171
        // reverse order.
172
        DecryptError(encryptedData, attrData []byte) (*sphinx.DecryptedError,
173
                error)
174
}
175

176
// SphinxErrorDecrypter wraps the sphinx data SphinxErrorDecrypter and maps the
177
// returned errors to concrete lnwire.FailureMessage instances.
178
type SphinxErrorDecrypter struct {
179
        decrypter *sphinx.OnionErrorDecrypter
180
}
181

182
// NewSphinxErrorDecrypter instantiates a new error decrypter.
183
func NewSphinxErrorDecrypter(circuit *sphinx.Circuit) *SphinxErrorDecrypter {
38✔
184
        return &SphinxErrorDecrypter{
38✔
185
                decrypter: sphinx.NewOnionErrorDecrypter(
38✔
186
                        circuit, hop.AttrErrorStruct,
38✔
187
                ),
38✔
188
        }
38✔
189
}
38✔
190

191
// DecryptError peels off each layer of onion encryption from the first hop, to
192
// the source of the error. A fully populated lnwire.FailureMessage is returned
193
// along with the source of the error.
194
//
195
// NOTE: Part of the ErrorDecrypter interface.
196
func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason,
197
        attrData []byte) (*ForwardingError, error) {
5✔
198

5✔
199
        // We do not set the strict attribution flag, as we want to account for
5✔
200
        // the grace period during which nodes are still upgrading to support
5✔
201
        // this feature. If set prematurely it can lead to early blame of our
5✔
202
        // direct peers that may not support this feature yet, blacklisting our
5✔
203
        // channels and failing our payments.
5✔
204
        attrErr, err := s.decrypter.DecryptError(reason, attrData, false)
5✔
205
        if err != nil {
6✔
206
                return nil, err
1✔
207
        }
1✔
208

209
        var holdTimes []string
4✔
210
        for _, payload := range attrErr.HoldTimes {
7✔
211
                // Read hold time.
3✔
212
                holdTime := payload
3✔
213

3✔
214
                holdTimes = append(
3✔
215
                        holdTimes,
3✔
216
                        fmt.Sprintf("%vms", holdTime*100),
3✔
217
                )
3✔
218
        }
3✔
219

220
        // For now just log the hold times, the collector of the payment result
221
        // should handle this in a more sophisticated way.
222
        log.Debugf("Extracted hold times from onion error: %v",
4✔
223
                strings.Join(holdTimes, "/"))
4✔
224

4✔
225
        // Decode the failure. If an error occurs, we leave the failure message
4✔
226
        // field nil.
4✔
227
        r := bytes.NewReader(attrErr.Message)
4✔
228
        failureMsg, err := lnwire.DecodeFailure(r, 0)
4✔
229
        if err != nil {
4✔
NEW
230
                return NewUnknownForwardingError(attrErr.SenderIdx), nil
×
UNCOV
231
        }
×
232

233
        return NewForwardingError(
4✔
234
                failureMsg, attrErr.SenderIdx, attrErr.HoldTimes,
4✔
235
        ), nil
4✔
236
}
237

238
// A compile time check to ensure ErrorDecrypter implements the Deobfuscator
239
// interface.
240
var _ ErrorDecrypter = (*SphinxErrorDecrypter)(nil)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc