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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

94.29
/routing/chainview/queue.go
1
package chainview
2

3
import "sync"
4

5
// blockEventType is the possible types of a blockEvent.
6
type blockEventType uint8
7

8
const (
9
        // connected is the type of a blockEvent representing a block
10
        // that was connected to our current chain.
11
        connected blockEventType = iota
12

13
        // disconnected is the type of a blockEvent representing a
14
        // block that is stale/disconnected from our current chain.
15
        disconnected
16
)
17

18
// blockEvent represent a block that was either connected
19
// or disconnected from the current chain.
20
type blockEvent struct {
21
        eventType blockEventType
22
        block     *FilteredBlock
23
}
24

25
// blockEventQueue is an ordered queue for block events sent from a
26
// FilteredChainView. The two types of possible block events are
27
// connected/new blocks, and disconnected/stale blocks. The
28
// blockEventQueue keeps the order of these events intact, while
29
// still being non-blocking. This is important in order for the
30
// chainView's call to onBlockConnected/onBlockDisconnected to not
31
// get blocked, and for the consumer of the block events to always
32
// get the events in the correct order.
33
type blockEventQueue struct {
34
        queueCond *sync.Cond
35
        queueMtx  sync.Mutex
36
        queue     []*blockEvent
37

38
        // newBlocks is the channel where the consumer of the queue
39
        // will receive connected/new blocks from the FilteredChainView.
40
        newBlocks chan *FilteredBlock
41

42
        // staleBlocks is the channel where the consumer of the queue will
43
        // receive disconnected/stale blocks from the FilteredChainView.
44
        staleBlocks chan *FilteredBlock
45

46
        wg   sync.WaitGroup
47
        quit chan struct{}
48
}
49

50
// newBlockEventQueue creates a new blockEventQueue.
51
func newBlockEventQueue() *blockEventQueue {
3✔
52
        b := &blockEventQueue{
3✔
53
                newBlocks:   make(chan *FilteredBlock),
3✔
54
                staleBlocks: make(chan *FilteredBlock),
3✔
55
                quit:        make(chan struct{}),
3✔
56
        }
3✔
57
        b.queueCond = sync.NewCond(&b.queueMtx)
3✔
58

3✔
59
        return b
3✔
60
}
3✔
61

62
// Start starts the blockEventQueue coordinator such that it can start handling
63
// events.
64
func (b *blockEventQueue) Start() {
3✔
65
        b.wg.Add(1)
3✔
66
        go b.queueCoordinator()
3✔
67
}
3✔
68

69
// Stop signals the queue coordinator to stop, such that the queue can be
70
// shut down.
71
func (b *blockEventQueue) Stop() {
3✔
72
        close(b.quit)
3✔
73

3✔
74
        b.queueCond.Signal()
3✔
75
}
3✔
76

77
// queueCoordinator is the queue's main loop, handling incoming block events
78
// and handing them off to the correct output channel.
79
//
80
// NB: MUST be run as a goroutine from the Start() method.
81
func (b *blockEventQueue) queueCoordinator() {
3✔
82
        defer b.wg.Done()
3✔
83

3✔
84
        for {
6✔
85
                // First, we'll check our condition. If the queue of events is
3✔
86
                // empty, then we'll wait until a new item is added.
3✔
87
                b.queueCond.L.Lock()
3✔
88
                for len(b.queue) == 0 {
6✔
89
                        b.queueCond.Wait()
3✔
90

3✔
91
                        // If we were woke up in order to exit, then we'll do
3✔
92
                        // so. Otherwise, we'll check the queue for any new
3✔
93
                        // items.
3✔
94
                        select {
3✔
95
                        case <-b.quit:
3✔
96
                                b.queueCond.L.Unlock()
3✔
97
                                return
3✔
98
                        default:
3✔
99
                        }
100
                }
101

102
                // Grab the first element in the queue, and nil the index to
103
                // avoid gc leak.
104
                event := b.queue[0]
3✔
105
                b.queue[0] = nil
3✔
106
                b.queue = b.queue[1:]
3✔
107
                b.queueCond.L.Unlock()
3✔
108

3✔
109
                // In the case this is a connected block, we'll send it on the
3✔
110
                // newBlocks channel. In case it is a disconnected block, we'll
3✔
111
                // send it on the staleBlocks channel. This send will block
3✔
112
                // until it is received by the consumer on the other end, making
3✔
113
                // sure we won't try to send any other block event before the
3✔
114
                // consumer is aware of this one.
3✔
115
                switch event.eventType {
3✔
116
                case connected:
3✔
117
                        select {
3✔
118
                        case b.newBlocks <- event.block:
3✔
UNCOV
119
                        case <-b.quit:
×
UNCOV
120
                                return
×
121
                        }
122
                case disconnected:
2✔
123
                        select {
2✔
124
                        case b.staleBlocks <- event.block:
2✔
125
                        case <-b.quit:
×
126
                                return
×
127
                        }
128
                }
129
        }
130
}
131

132
// Add puts the provided blockEvent at the end of the event queue, making sure
133
// it will first be received after all previous events. This method is
134
// non-blocking, in the sense that it will never wait for the consumer of the
135
// queue to read form the other end, making it safe to call from the
136
// FilteredChainView's onBlockConnected/onBlockDisconnected.
137
func (b *blockEventQueue) Add(event *blockEvent) {
3✔
138

3✔
139
        // Lock the condition, and add the event to the end of queue.
3✔
140
        b.queueCond.L.Lock()
3✔
141
        b.queue = append(b.queue, event)
3✔
142
        b.queueCond.L.Unlock()
3✔
143

3✔
144
        // With the event added, we signal to the queueCoordinator that
3✔
145
        // there are new events to handle.
3✔
146
        b.queueCond.Signal()
3✔
147
}
3✔
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