• 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

89.39
/graph/validation_barrier.go
1
package graph
2

3
import (
4
        "fmt"
5
        "sync"
6

7
        "github.com/lightningnetwork/lnd/graph/db/models"
8
        "github.com/lightningnetwork/lnd/lnwire"
9
        "github.com/lightningnetwork/lnd/routing/route"
10
)
11

12
// validationSignals contains two signals which allows the ValidationBarrier to
13
// communicate back to the caller whether a dependent should be processed or not
14
// based on whether its parent was successfully validated. Only one of these
15
// signals is to be used at a time.
16
type validationSignals struct {
17
        // allow is the signal used to allow a dependent to be processed.
18
        allow chan struct{}
19

20
        // deny is the signal used to prevent a dependent from being processed.
21
        deny chan struct{}
22
}
23

24
// ValidationBarrier is a barrier used to ensure proper validation order while
25
// concurrently validating new announcements for channel edges, and the
26
// attributes of channel edges.  It uses this set of maps (protected by this
27
// mutex) to track validation dependencies. For a given channel our
28
// dependencies look like this: chanAnn <- chanUp <- nodeAnn. That is we must
29
// validate the item on the left of the arrow before that on the right.
30
type ValidationBarrier struct {
31
        // validationSemaphore is a channel of structs which is used as a
32
        // semaphore. Initially we'll fill this with a buffered channel of the
33
        // size of the number of active requests. Each new job will consume
34
        // from this channel, then restore the value upon completion.
35
        validationSemaphore chan struct{}
36

37
        // chanAnnFinSignal is map that keep track of all the pending
38
        // ChannelAnnouncement like validation job going on. Once the job has
39
        // been completed, the channel will be closed unblocking any
40
        // dependants.
41
        chanAnnFinSignal map[lnwire.ShortChannelID]*validationSignals
42

43
        // chanEdgeDependencies tracks any channel edge updates which should
44
        // wait until the completion of the ChannelAnnouncement before
45
        // proceeding. This is a dependency, as we can't validate the update
46
        // before we validate the announcement which creates the channel
47
        // itself.
48
        chanEdgeDependencies map[lnwire.ShortChannelID]*validationSignals
49

50
        // nodeAnnDependencies tracks any pending NodeAnnouncement validation
51
        // jobs which should wait until the completion of the
52
        // ChannelAnnouncement before proceeding.
53
        nodeAnnDependencies map[route.Vertex]*validationSignals
54

55
        quit chan struct{}
56
        sync.Mutex
57
}
58

59
// NewValidationBarrier creates a new instance of a validation barrier given
60
// the total number of active requests, and a quit channel which will be used
61
// to know when to kill pending, but unfilled jobs.
62
func NewValidationBarrier(numActiveReqs int,
63
        quitChan chan struct{}) *ValidationBarrier {
1✔
64

1✔
65
        v := &ValidationBarrier{
1✔
66
                chanAnnFinSignal:     make(map[lnwire.ShortChannelID]*validationSignals),
1✔
67
                chanEdgeDependencies: make(map[lnwire.ShortChannelID]*validationSignals),
1✔
68
                nodeAnnDependencies:  make(map[route.Vertex]*validationSignals),
1✔
69
                quit:                 quitChan,
1✔
70
        }
1✔
71

1✔
72
        // We'll first initialize a set of semaphores to limit our concurrency
1✔
73
        // when validating incoming requests in parallel.
1✔
74
        v.validationSemaphore = make(chan struct{}, numActiveReqs)
1✔
75
        for i := 0; i < numActiveReqs; i++ {
2✔
76
                v.validationSemaphore <- struct{}{}
1✔
77
        }
1✔
78

79
        return v
1✔
80
}
81

82
// InitJobDependencies will wait for a new job slot to become open, and then
83
// sets up any dependent signals/trigger for the new job
84
func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
1✔
85
        // We'll wait for either a new slot to become open, or for the quit
1✔
86
        // channel to be closed.
1✔
87
        select {
1✔
88
        case <-v.validationSemaphore:
1✔
89
        case <-v.quit:
×
90
        }
91

92
        v.Lock()
1✔
93
        defer v.Unlock()
1✔
94

1✔
95
        // Once a slot is open, we'll examine the message of the job, to see if
1✔
96
        // there need to be any dependent barriers set up.
1✔
97
        switch msg := job.(type) {
1✔
98

99
        // If this is a channel announcement, then we'll need to set up den
100
        // tenancies, as we'll need to verify this before we verify any
101
        // ChannelUpdates for the same channel, or NodeAnnouncements of nodes
102
        // that are involved in this channel. This goes for both the wire
103
        // type,s and also the types that we use within the database.
104
        case *lnwire.ChannelAnnouncement1:
1✔
105

1✔
106
                // We ensure that we only create a new announcement signal iff,
1✔
107
                // one doesn't already exist, as there may be duplicate
1✔
108
                // announcements.  We'll close this signal once the
1✔
109
                // ChannelAnnouncement has been validated. This will result in
1✔
110
                // all the dependent jobs being unlocked so they can finish
1✔
111
                // execution themselves.
1✔
112
                if _, ok := v.chanAnnFinSignal[msg.ShortChannelID]; !ok {
2✔
113
                        // We'll create the channel that we close after we
1✔
114
                        // validate this announcement. All dependants will
1✔
115
                        // point to this same channel, so they'll be unblocked
1✔
116
                        // at the same time.
1✔
117
                        signals := &validationSignals{
1✔
118
                                allow: make(chan struct{}),
1✔
119
                                deny:  make(chan struct{}),
1✔
120
                        }
1✔
121

1✔
122
                        v.chanAnnFinSignal[msg.ShortChannelID] = signals
1✔
123
                        v.chanEdgeDependencies[msg.ShortChannelID] = signals
1✔
124

1✔
125
                        v.nodeAnnDependencies[route.Vertex(msg.NodeID1)] = signals
1✔
126
                        v.nodeAnnDependencies[route.Vertex(msg.NodeID2)] = signals
1✔
127
                }
1✔
128
        case *models.ChannelEdgeInfo:
1✔
129

1✔
130
                shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
1✔
131
                if _, ok := v.chanAnnFinSignal[shortID]; !ok {
2✔
132
                        signals := &validationSignals{
1✔
133
                                allow: make(chan struct{}),
1✔
134
                                deny:  make(chan struct{}),
1✔
135
                        }
1✔
136

1✔
137
                        v.chanAnnFinSignal[shortID] = signals
1✔
138
                        v.chanEdgeDependencies[shortID] = signals
1✔
139

1✔
140
                        v.nodeAnnDependencies[route.Vertex(msg.NodeKey1Bytes)] = signals
1✔
141
                        v.nodeAnnDependencies[route.Vertex(msg.NodeKey2Bytes)] = signals
1✔
142
                }
1✔
143

144
        // These other types don't have any dependants, so no further
145
        // initialization needs to be done beyond just occupying a job slot.
146
        case *models.ChannelEdgePolicy:
1✔
147
                return
1✔
148
        case *lnwire.ChannelUpdate1:
1✔
149
                return
1✔
150
        case *lnwire.NodeAnnouncement:
1✔
151
                // TODO(roasbeef): node ann needs to wait on existing channel updates
1✔
152
                return
1✔
153
        case *models.LightningNode:
1✔
154
                return
1✔
155
        case *lnwire.AnnounceSignatures1:
×
156
                // TODO(roasbeef): need to wait on chan ann?
×
157
                return
×
158
        }
159
}
160

161
// CompleteJob returns a free slot to the set of available job slots. This
162
// should be called once a job has been fully completed. Otherwise, slots may
163
// not be returned to the internal scheduling, causing a deadlock when a new
164
// overflow job is attempted.
165
func (v *ValidationBarrier) CompleteJob() {
1✔
166
        select {
1✔
167
        case v.validationSemaphore <- struct{}{}:
1✔
168
        case <-v.quit:
×
169
        }
170
}
171

172
// WaitForDependants will block until any jobs that this job dependants on have
173
// finished executing. This allows us a graceful way to schedule goroutines
174
// based on any pending uncompleted dependent jobs. If this job doesn't have an
175
// active dependent, then this function will return immediately.
176
func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
1✔
177

1✔
178
        var (
1✔
179
                signals *validationSignals
1✔
180
                ok      bool
1✔
181
                jobDesc string
1✔
182
        )
1✔
183

1✔
184
        // Acquire a lock to read ValidationBarrier.
1✔
185
        v.Lock()
1✔
186

1✔
187
        switch msg := job.(type) {
1✔
188
        // Any ChannelUpdate or NodeAnnouncement jobs will need to wait on the
189
        // completion of any active ChannelAnnouncement jobs related to them.
190
        case *models.ChannelEdgePolicy:
1✔
191
                shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
1✔
192
                signals, ok = v.chanEdgeDependencies[shortID]
1✔
193

1✔
194
                jobDesc = fmt.Sprintf("job=lnwire.ChannelEdgePolicy, scid=%v",
1✔
195
                        msg.ChannelID)
1✔
196

197
        case *models.LightningNode:
1✔
198
                vertex := route.Vertex(msg.PubKeyBytes)
1✔
199
                signals, ok = v.nodeAnnDependencies[vertex]
1✔
200

1✔
201
                jobDesc = fmt.Sprintf("job=channeldb.LightningNode, pub=%s",
1✔
202
                        vertex)
1✔
203

204
        case *lnwire.ChannelUpdate1:
1✔
205
                signals, ok = v.chanEdgeDependencies[msg.ShortChannelID]
1✔
206

1✔
207
                jobDesc = fmt.Sprintf("job=lnwire.ChannelUpdate, scid=%v",
1✔
208
                        msg.ShortChannelID.ToUint64())
1✔
209

210
        case *lnwire.NodeAnnouncement:
1✔
211
                vertex := route.Vertex(msg.NodeID)
1✔
212
                signals, ok = v.nodeAnnDependencies[vertex]
1✔
213
                jobDesc = fmt.Sprintf("job=lnwire.NodeAnnouncement, pub=%s",
1✔
214
                        vertex)
1✔
215

216
        // Other types of jobs can be executed immediately, so we'll just
217
        // return directly.
218
        case *lnwire.AnnounceSignatures1:
×
219
                // TODO(roasbeef): need to wait on chan ann?
220
        case *models.ChannelEdgeInfo:
1✔
221
        case *lnwire.ChannelAnnouncement1:
1✔
222
        }
223

224
        // Release the lock once the above read is finished.
225
        v.Unlock()
1✔
226

1✔
227
        // If it's not ok, it means either the job is not a dependent type, or
1✔
228
        // it doesn't have a dependency signal. Either way, we can return
1✔
229
        // early.
1✔
230
        if !ok {
2✔
231
                return nil
1✔
232
        }
1✔
233

234
        log.Debugf("Waiting for dependent on %s", jobDesc)
1✔
235

1✔
236
        // If we do have an active job, then we'll wait until either the signal
1✔
237
        // is closed, or the set of jobs exits.
1✔
238
        select {
1✔
UNCOV
239
        case <-v.quit:
×
UNCOV
240
                return NewErrf(ErrVBarrierShuttingDown,
×
UNCOV
241
                        "validation barrier shutting down")
×
242

UNCOV
243
        case <-signals.deny:
×
UNCOV
244
                log.Debugf("Signal deny for %s", jobDesc)
×
UNCOV
245
                return NewErrf(ErrParentValidationFailed,
×
UNCOV
246
                        "parent validation failed")
×
247

248
        case <-signals.allow:
1✔
249
                log.Tracef("Signal allow for %s", jobDesc)
1✔
250
                return nil
1✔
251
        }
252
}
253

254
// SignalDependants will allow/deny any jobs that are dependent on this job that
255
// they can continue execution. If the job doesn't have any dependants, then
256
// this function sill exit immediately.
257
func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) {
1✔
258
        v.Lock()
1✔
259
        defer v.Unlock()
1✔
260

1✔
261
        switch msg := job.(type) {
1✔
262

263
        // If we've just finished executing a ChannelAnnouncement, then we'll
264
        // close out the signal, and remove the signal from the map of active
265
        // ones. This will allow/deny any dependent jobs to continue execution.
266
        case *models.ChannelEdgeInfo:
1✔
267
                shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
1✔
268
                finSignals, ok := v.chanAnnFinSignal[shortID]
1✔
269
                if ok {
2✔
270
                        if allow {
2✔
271
                                close(finSignals.allow)
1✔
272
                        } else {
1✔
UNCOV
273
                                close(finSignals.deny)
×
UNCOV
274
                        }
×
275
                        delete(v.chanAnnFinSignal, shortID)
1✔
276
                }
277
        case *lnwire.ChannelAnnouncement1:
1✔
278
                finSignals, ok := v.chanAnnFinSignal[msg.ShortChannelID]
1✔
279
                if ok {
2✔
280
                        if allow {
2✔
281
                                close(finSignals.allow)
1✔
282
                        } else {
1✔
UNCOV
283
                                close(finSignals.deny)
×
UNCOV
284
                        }
×
285
                        delete(v.chanAnnFinSignal, msg.ShortChannelID)
1✔
286
                }
287

288
                delete(v.chanEdgeDependencies, msg.ShortChannelID)
1✔
289

290
        // For all other job types, we'll delete the tracking entries from the
291
        // map, as if we reach this point, then all dependants have already
292
        // finished executing and we can proceed.
293
        case *models.LightningNode:
1✔
294
                delete(v.nodeAnnDependencies, route.Vertex(msg.PubKeyBytes))
1✔
295
        case *lnwire.NodeAnnouncement:
1✔
296
                delete(v.nodeAnnDependencies, route.Vertex(msg.NodeID))
1✔
297
        case *lnwire.ChannelUpdate1:
1✔
298
                delete(v.chanEdgeDependencies, msg.ShortChannelID)
1✔
299
        case *models.ChannelEdgePolicy:
1✔
300
                shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
1✔
301
                delete(v.chanEdgeDependencies, shortID)
1✔
302

303
        case *lnwire.AnnounceSignatures1:
×
304
                return
×
305
        }
306
}
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