• 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

75.81
/htlcswitch/circuit.go
1
package htlcswitch
2

3
import (
4
        "encoding/binary"
5
        "io"
6

7
        "github.com/lightningnetwork/lnd/channeldb"
8
        "github.com/lightningnetwork/lnd/graph/db/models"
9
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
// EmptyCircuitKey is a default value for an outgoing circuit key returned when
14
// a circuit's keystone has not been set. Note that this value is invalid for
15
// use as a keystone, since the outgoing channel id can never be equal to
16
// sourceHop.
17
var EmptyCircuitKey CircuitKey
18

19
// CircuitKey is a tuple of channel ID and HTLC ID, used to uniquely identify
20
// HTLCs in a circuit. Circuits are identified primarily by the circuit key of
21
// the incoming HTLC. However, a circuit may also be referenced by its outgoing
22
// circuit key after the HTLC has been forwarded via the outgoing link.
23
type CircuitKey = models.CircuitKey
24

25
// PaymentCircuit is used by the switch as placeholder between when the
26
// switch makes a forwarding decision and the outgoing link determines the
27
// proper HTLC ID for the local log. After the outgoing HTLC ID has been
28
// determined, the half circuit will be converted into a full PaymentCircuit.
29
type PaymentCircuit struct {
30
        // AddRef is the forward reference of the Add update in the incoming
31
        // link's forwarding package. This value is set on the htlcPacket of the
32
        // returned settle/fail so that it can be removed from disk.
33
        AddRef channeldb.AddRef
34

35
        // Incoming is the circuit key identifying the incoming channel and htlc
36
        // index from which this ADD originates.
37
        Incoming CircuitKey
38

39
        // Outgoing is the circuit key identifying the outgoing channel, and the
40
        // HTLC index that was used to forward the ADD. It will be nil if this
41
        // circuit's keystone has not been set.
42
        Outgoing *CircuitKey
43

44
        // PaymentHash used as unique identifier of payment.
45
        PaymentHash [32]byte
46

47
        // IncomingAmount is the value of the HTLC from the incoming link.
48
        IncomingAmount lnwire.MilliSatoshi
49

50
        // OutgoingAmount specifies the value of the HTLC leaving the switch,
51
        // either as a payment or forwarded amount.
52
        OutgoingAmount lnwire.MilliSatoshi
53

54
        // ErrorEncrypter is used to re-encrypt the onion failure before
55
        // sending it back to the originator of the payment.
56
        ErrorEncrypter hop.ErrorEncrypter
57

58
        // LoadedFromDisk is set true for any circuits loaded after the circuit
59
        // map is reloaded from disk.
60
        //
61
        // NOTE: This value is determined implicitly during a restart. It is not
62
        // persisted, and should never be set outside the circuit map.
63
        LoadedFromDisk bool
64
}
65

66
// HasKeystone returns true if an outgoing link has assigned this circuit's
67
// outgoing circuit key.
68
func (c *PaymentCircuit) HasKeystone() bool {
791✔
69
        return c.Outgoing != nil
791✔
70
}
791✔
71

72
// newPaymentCircuit initializes a payment circuit on the heap using the payment
73
// hash and an in-memory htlc packet.
74
func newPaymentCircuit(hash *[32]byte, pkt *htlcPacket) *PaymentCircuit {
496✔
75
        var addRef channeldb.AddRef
496✔
76
        if pkt.sourceRef != nil {
534✔
77
                addRef = *pkt.sourceRef
38✔
78
        }
38✔
79

80
        return &PaymentCircuit{
496✔
81
                AddRef: addRef,
496✔
82
                Incoming: CircuitKey{
496✔
83
                        ChanID: pkt.incomingChanID,
496✔
84
                        HtlcID: pkt.incomingHTLCID,
496✔
85
                },
496✔
86
                PaymentHash:    *hash,
496✔
87
                IncomingAmount: pkt.incomingAmount,
496✔
88
                OutgoingAmount: pkt.amount,
496✔
89
                ErrorEncrypter: pkt.obfuscator,
496✔
90
        }
496✔
91
}
92

93
// makePaymentCircuit initializes a payment circuit on the stack using the
94
// payment hash and an in-memory htlc packet.
95
func makePaymentCircuit(hash *[32]byte, pkt *htlcPacket) PaymentCircuit {
15✔
96
        var addRef channeldb.AddRef
15✔
97
        if pkt.sourceRef != nil {
15✔
98
                addRef = *pkt.sourceRef
×
99
        }
×
100

101
        return PaymentCircuit{
15✔
102
                AddRef: addRef,
15✔
103
                Incoming: CircuitKey{
15✔
104
                        ChanID: pkt.incomingChanID,
15✔
105
                        HtlcID: pkt.incomingHTLCID,
15✔
106
                },
15✔
107
                PaymentHash:    *hash,
15✔
108
                IncomingAmount: pkt.incomingAmount,
15✔
109
                OutgoingAmount: pkt.amount,
15✔
110
                ErrorEncrypter: pkt.obfuscator,
15✔
111
        }
15✔
112
}
113

114
// Encode writes a PaymentCircuit to the provided io.Writer.
115
func (c *PaymentCircuit) Encode(w io.Writer) error {
581✔
116
        if err := c.AddRef.Encode(w); err != nil {
581✔
117
                return err
×
118
        }
×
119

120
        if err := c.Incoming.Encode(w); err != nil {
581✔
121
                return err
×
122
        }
×
123

124
        if _, err := w.Write(c.PaymentHash[:]); err != nil {
581✔
125
                return err
×
126
        }
×
127

128
        var scratch [8]byte
581✔
129

581✔
130
        binary.BigEndian.PutUint64(scratch[:], uint64(c.IncomingAmount))
581✔
131
        if _, err := w.Write(scratch[:]); err != nil {
581✔
132
                return err
×
133
        }
×
134

135
        binary.BigEndian.PutUint64(scratch[:], uint64(c.OutgoingAmount))
581✔
136
        if _, err := w.Write(scratch[:]); err != nil {
581✔
137
                return err
×
138
        }
×
139

140
        // Defaults to EncrypterTypeNone.
141
        var encrypterType hop.EncrypterType
581✔
142
        if c.ErrorEncrypter != nil {
724✔
143
                encrypterType = c.ErrorEncrypter.Type()
143✔
144
        }
143✔
145

146
        err := binary.Write(w, binary.BigEndian, encrypterType)
581✔
147
        if err != nil {
581✔
148
                return err
×
149
        }
×
150

151
        // Skip encoding of error encrypter if this half add does not have one.
152
        if encrypterType == hop.EncrypterTypeNone {
1,022✔
153
                return nil
441✔
154
        }
441✔
155

156
        return c.ErrorEncrypter.Encode(w)
143✔
157
}
158

159
// Decode reads a PaymentCircuit from the provided io.Reader.
160
func (c *PaymentCircuit) Decode(r io.Reader) error {
105✔
161
        if err := c.AddRef.Decode(r); err != nil {
105✔
162
                return err
×
163
        }
×
164

165
        if err := c.Incoming.Decode(r); err != nil {
105✔
166
                return err
×
167
        }
×
168

169
        if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil {
105✔
170
                return err
×
171
        }
×
172

173
        var scratch [8]byte
105✔
174

105✔
175
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
105✔
176
                return err
×
177
        }
×
178
        c.IncomingAmount = lnwire.MilliSatoshi(
105✔
179
                binary.BigEndian.Uint64(scratch[:]))
105✔
180

105✔
181
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
105✔
182
                return err
×
183
        }
×
184
        c.OutgoingAmount = lnwire.MilliSatoshi(
105✔
185
                binary.BigEndian.Uint64(scratch[:]))
105✔
186

105✔
187
        // Read the encrypter type used for this circuit.
105✔
188
        var encrypterType hop.EncrypterType
105✔
189
        err := binary.Read(r, binary.BigEndian, &encrypterType)
105✔
190
        if err != nil {
105✔
191
                return err
×
192
        }
×
193

194
        switch encrypterType {
105✔
195
        case hop.EncrypterTypeNone:
7✔
196
                // No encrypter was provided, such as when the payment is
7✔
197
                // locally initiated.
7✔
198
                return nil
7✔
199

200
        case hop.EncrypterTypeSphinx:
35✔
201
                // Sphinx encrypter was used as this is a forwarded HTLC.
35✔
202
                c.ErrorEncrypter = hop.NewSphinxErrorEncrypterUninitialized()
35✔
203

204
        case hop.EncrypterTypeMock:
66✔
205
                // Test encrypter.
66✔
206
                c.ErrorEncrypter = NewMockObfuscator()
66✔
207

208
        case hop.EncrypterTypeIntroduction:
3✔
209
                c.ErrorEncrypter =
3✔
210
                        hop.NewIntroductionErrorEncrypterUninitialized()
3✔
211

212
        case hop.EncrypterTypeRelaying:
×
NEW
213
                c.ErrorEncrypter = hop.NewRelayingErrorEncrypterUninitialized()
×
214

215
        default:
×
216
                return UnknownEncrypterType(encrypterType)
×
217
        }
218

219
        return c.ErrorEncrypter.Decode(r)
101✔
220
}
221

222
// InKey returns the primary identifier for the circuit corresponding to the
223
// incoming HTLC.
224
func (c *PaymentCircuit) InKey() CircuitKey {
1,537✔
225
        return c.Incoming
1,537✔
226
}
1,537✔
227

228
// OutKey returns the keystone identifying the outgoing link and HTLC ID. If the
229
// circuit hasn't been completed, this method returns an EmptyKeystone, which is
230
// an invalid outgoing circuit key. Only call this method if HasKeystone returns
231
// true.
232
func (c *PaymentCircuit) OutKey() CircuitKey {
1,629✔
233
        if c.Outgoing != nil {
3,239✔
234
                return *c.Outgoing
1,610✔
235
        }
1,610✔
236

237
        return EmptyCircuitKey
19✔
238
}
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