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

lightningnetwork / lnd / 12115442155

02 Dec 2024 08:28AM UTC coverage: 48.662% (-10.3%) from 58.948%
12115442155

Pull #9175

github

ellemouton
netann: update ChanAnn2 validation to work for P2WSH channels

This commit expands the ChannelAnnouncement2 validation for the case
where it is announcing a P2WSH channel.
Pull Request #9175: lnwire+netann: update structure of g175 messages to be pure TLV

6 of 314 new or added lines in 9 files covered. (1.91%)

27532 existing lines in 434 files now uncovered.

97890 of 201164 relevant lines covered (48.66%)

0.52 hits per line

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

59.85
/routing/bandwidth.go
1
package routing
2

3
import (
4
        "fmt"
5

6
        "github.com/lightningnetwork/lnd/fn"
7
        graphdb "github.com/lightningnetwork/lnd/graph/db"
8
        "github.com/lightningnetwork/lnd/htlcswitch"
9
        "github.com/lightningnetwork/lnd/lnwire"
10
        "github.com/lightningnetwork/lnd/routing/route"
11
        "github.com/lightningnetwork/lnd/tlv"
12
)
13

14
// bandwidthHints provides hints about the currently available balance in our
15
// channels.
16
type bandwidthHints interface {
17
        // availableChanBandwidth returns the total available bandwidth for a
18
        // channel and a bool indicating whether the channel hint was found.
19
        // The amount parameter is used to validate the outgoing htlc amount
20
        // that we wish to add to the channel against its flow restrictions. If
21
        // a zero amount is provided, the minimum htlc value for the channel
22
        // will be used. If the channel is unavailable, a zero amount is
23
        // returned.
24
        availableChanBandwidth(channelID uint64,
25
                amount lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool)
26

27
        // firstHopCustomBlob returns the custom blob for the first hop of the
28
        // payment, if available.
29
        firstHopCustomBlob() fn.Option[tlv.Blob]
30
}
31

32
// TlvTrafficShaper is an interface that allows the sender to determine if a
33
// payment should be carried by a channel based on the TLV records that may be
34
// present in the `update_add_htlc` message or the channel commitment itself.
35
type TlvTrafficShaper interface {
36
        AuxHtlcModifier
37

38
        // ShouldHandleTraffic is called in order to check if the channel
39
        // identified by the provided channel ID may have external mechanisms
40
        // that would allow it to carry out the payment.
41
        ShouldHandleTraffic(cid lnwire.ShortChannelID,
42
                fundingBlob fn.Option[tlv.Blob]) (bool, error)
43

44
        // PaymentBandwidth returns the available bandwidth for a custom channel
45
        // decided by the given channel aux blob and HTLC blob. A return value
46
        // of 0 means there is no bandwidth available. To find out if a channel
47
        // is a custom channel that should be handled by the traffic shaper, the
48
        // HandleTraffic method should be called first.
49
        PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob],
50
                linkBandwidth,
51
                htlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error)
52
}
53

54
// AuxHtlcModifier is an interface that allows the sender to modify the outgoing
55
// HTLC of a payment by changing the amount or the wire message tlv records.
56
type AuxHtlcModifier interface {
57
        // ProduceHtlcExtraData is a function that, based on the previous extra
58
        // data blob of an HTLC, may produce a different blob or modify the
59
        // amount of bitcoin this htlc should carry.
60
        ProduceHtlcExtraData(totalAmount lnwire.MilliSatoshi,
61
                htlcCustomRecords lnwire.CustomRecords) (lnwire.MilliSatoshi,
62
                lnwire.CustomRecords, error)
63
}
64

65
// getLinkQuery is the function signature used to lookup a link.
66
type getLinkQuery func(lnwire.ShortChannelID) (
67
        htlcswitch.ChannelLink, error)
68

69
// bandwidthManager is an implementation of the bandwidthHints interface which
70
// uses the link lookup provided to query the link for our latest local channel
71
// balances.
72
type bandwidthManager struct {
73
        getLink       getLinkQuery
74
        localChans    map[lnwire.ShortChannelID]struct{}
75
        firstHopBlob  fn.Option[tlv.Blob]
76
        trafficShaper fn.Option[TlvTrafficShaper]
77
}
78

79
// newBandwidthManager creates a bandwidth manager for the source node provided
80
// which is used to obtain hints from the lower layer w.r.t the available
81
// bandwidth of edges on the network. Currently, we'll only obtain bandwidth
82
// hints for the edges we directly have open ourselves. Obtaining these hints
83
// allows us to reduce the number of extraneous attempts as we can skip channels
84
// that are inactive, or just don't have enough bandwidth to carry the payment.
85
func newBandwidthManager(graph Graph, sourceNode route.Vertex,
86
        linkQuery getLinkQuery, firstHopBlob fn.Option[tlv.Blob],
87
        trafficShaper fn.Option[TlvTrafficShaper]) (*bandwidthManager, error) {
1✔
88

1✔
89
        manager := &bandwidthManager{
1✔
90
                getLink:       linkQuery,
1✔
91
                localChans:    make(map[lnwire.ShortChannelID]struct{}),
1✔
92
                firstHopBlob:  firstHopBlob,
1✔
93
                trafficShaper: trafficShaper,
1✔
94
        }
1✔
95

1✔
96
        // First, we'll collect the set of outbound edges from the target
1✔
97
        // source node and add them to our bandwidth manager's map of channels.
1✔
98
        err := graph.ForEachNodeChannel(sourceNode,
1✔
99
                func(channel *graphdb.DirectedChannel) error {
2✔
100
                        shortID := lnwire.NewShortChanIDFromInt(
1✔
101
                                channel.ChannelID,
1✔
102
                        )
1✔
103
                        manager.localChans[shortID] = struct{}{}
1✔
104

1✔
105
                        return nil
1✔
106
                })
1✔
107

108
        if err != nil {
1✔
109
                return nil, err
×
110
        }
×
111

112
        return manager, nil
1✔
113
}
114

115
// getBandwidth queries the current state of a link and gets its currently
116
// available bandwidth. Note that this function assumes that the channel being
117
// queried is one of our local channels, so any failure to retrieve the link
118
// is interpreted as the link being offline.
119
func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
120
        amount lnwire.MilliSatoshi) lnwire.MilliSatoshi {
1✔
121

1✔
122
        link, err := b.getLink(cid)
1✔
123
        if err != nil {
2✔
124
                // If the link isn't online, then we'll report that it has
1✔
125
                // zero bandwidth.
1✔
126
                log.Warnf("ShortChannelID=%v: link not found: %v", cid, err)
1✔
127
                return 0
1✔
128
        }
1✔
129

130
        // If the link is found within the switch, but it isn't yet eligible
131
        // to forward any HTLCs, then we'll treat it as if it isn't online in
132
        // the first place.
133
        if !link.EligibleToForward() {
2✔
134
                log.Warnf("ShortChannelID=%v: not eligible to forward", cid)
1✔
135
                return 0
1✔
136
        }
1✔
137

138
        // bandwidthResult is an inline type that we'll use to pass the
139
        // bandwidth result from the external traffic shaper to the main logic
140
        // below.
141
        type bandwidthResult struct {
1✔
142
                // bandwidth is the available bandwidth for the channel as
1✔
143
                // reported by the external traffic shaper. If the external
1✔
144
                // traffic shaper is not handling the channel, this value will
1✔
145
                // be fn.None
1✔
146
                bandwidth fn.Option[lnwire.MilliSatoshi]
1✔
147

1✔
148
                // htlcAmount is the amount we're going to use to check if we
1✔
149
                // can add another HTLC to the channel. If the external traffic
1✔
150
                // shaper is handling the channel, we'll use 0 to just sanity
1✔
151
                // check the number of HTLCs on the channel, since we don't know
1✔
152
                // the actual HTLC amount that will be sent.
1✔
153
                htlcAmount fn.Option[lnwire.MilliSatoshi]
1✔
154
        }
1✔
155

1✔
156
        var (
1✔
157
                // We will pass the link bandwidth to the external traffic
1✔
158
                // shaper. This is the current best estimate for the available
1✔
159
                // bandwidth for the link.
1✔
160
                linkBandwidth = link.Bandwidth()
1✔
161

1✔
162
                bandwidthErr = func(err error) fn.Result[bandwidthResult] {
1✔
163
                        return fn.Err[bandwidthResult](err)
×
164
                }
×
165
        )
166

167
        result, err := fn.MapOptionZ(
1✔
168
                b.trafficShaper,
1✔
169
                func(ts TlvTrafficShaper) fn.Result[bandwidthResult] {
1✔
UNCOV
170
                        fundingBlob := link.FundingCustomBlob()
×
UNCOV
171
                        shouldHandle, err := ts.ShouldHandleTraffic(
×
UNCOV
172
                                cid, fundingBlob,
×
UNCOV
173
                        )
×
UNCOV
174
                        if err != nil {
×
175
                                return bandwidthErr(fmt.Errorf("traffic "+
×
176
                                        "shaper failed to decide whether to "+
×
177
                                        "handle traffic: %w", err))
×
178
                        }
×
179

UNCOV
180
                        log.Debugf("ShortChannelID=%v: external traffic "+
×
UNCOV
181
                                "shaper is handling traffic: %v", cid,
×
UNCOV
182
                                shouldHandle)
×
UNCOV
183

×
UNCOV
184
                        // If this channel isn't handled by the external traffic
×
UNCOV
185
                        // shaper, we'll return early.
×
UNCOV
186
                        if !shouldHandle {
×
187
                                return fn.Ok(bandwidthResult{})
×
188
                        }
×
189

190
                        // Ask for a specific bandwidth to be used for the
191
                        // channel.
UNCOV
192
                        commitmentBlob := link.CommitmentCustomBlob()
×
UNCOV
193
                        auxBandwidth, err := ts.PaymentBandwidth(
×
UNCOV
194
                                b.firstHopBlob, commitmentBlob, linkBandwidth,
×
UNCOV
195
                                amount,
×
UNCOV
196
                        )
×
UNCOV
197
                        if err != nil {
×
198
                                return bandwidthErr(fmt.Errorf("failed to get "+
×
199
                                        "bandwidth from external traffic "+
×
200
                                        "shaper: %w", err))
×
201
                        }
×
202

UNCOV
203
                        log.Debugf("ShortChannelID=%v: external traffic "+
×
UNCOV
204
                                "shaper reported available bandwidth: %v", cid,
×
UNCOV
205
                                auxBandwidth)
×
UNCOV
206

×
UNCOV
207
                        // We don't know the actual HTLC amount that will be
×
UNCOV
208
                        // sent using the custom channel. But we'll still want
×
UNCOV
209
                        // to make sure we can add another HTLC, using the
×
UNCOV
210
                        // MayAddOutgoingHtlc method below. Passing 0 into that
×
UNCOV
211
                        // method will use the minimum HTLC value for the
×
UNCOV
212
                        // channel, which is okay to just check we don't exceed
×
UNCOV
213
                        // the max number of HTLCs on the channel. A proper
×
UNCOV
214
                        // balance check is done elsewhere.
×
UNCOV
215
                        return fn.Ok(bandwidthResult{
×
UNCOV
216
                                bandwidth:  fn.Some(auxBandwidth),
×
UNCOV
217
                                htlcAmount: fn.Some[lnwire.MilliSatoshi](0),
×
UNCOV
218
                        })
×
219
                },
220
        ).Unpack()
221
        if err != nil {
1✔
222
                log.Errorf("ShortChannelID=%v: failed to get bandwidth from "+
×
223
                        "external traffic shaper: %v", cid, err)
×
224

×
225
                return 0
×
226
        }
×
227

228
        htlcAmount := result.htlcAmount.UnwrapOr(amount)
1✔
229

1✔
230
        // If our link isn't currently in a state where it can add another
1✔
231
        // outgoing htlc, treat the link as unusable.
1✔
232
        if err := link.MayAddOutgoingHtlc(htlcAmount); err != nil {
2✔
233
                log.Warnf("ShortChannelID=%v: cannot add outgoing "+
1✔
234
                        "htlc with amount %v: %v", cid, htlcAmount, err)
1✔
235
                return 0
1✔
236
        }
1✔
237

238
        // If the external traffic shaper determined the bandwidth, we'll return
239
        // that value, even if it is zero (which would mean no bandwidth is
240
        // available on that channel).
241
        reportedBandwidth := result.bandwidth.UnwrapOr(linkBandwidth)
1✔
242

1✔
243
        return reportedBandwidth
1✔
244
}
245

246
// availableChanBandwidth returns the total available bandwidth for a channel
247
// and a bool indicating whether the channel hint was found. If the channel is
248
// unavailable, a zero amount is returned.
249
func (b *bandwidthManager) availableChanBandwidth(channelID uint64,
250
        amount lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool) {
1✔
251

1✔
252
        shortID := lnwire.NewShortChanIDFromInt(channelID)
1✔
253
        _, ok := b.localChans[shortID]
1✔
254
        if !ok {
1✔
UNCOV
255
                return 0, false
×
UNCOV
256
        }
×
257

258
        return b.getBandwidth(shortID, amount), true
1✔
259
}
260

261
// firstHopCustomBlob returns the custom blob for the first hop of the payment,
262
// if available.
263
func (b *bandwidthManager) firstHopCustomBlob() fn.Option[tlv.Blob] {
1✔
264
        return b.firstHopBlob
1✔
265
}
1✔
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