• 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

89.16
/lnwallet/update_log.go
1
package lnwallet
2

3
import (
4
        "github.com/lightningnetwork/lnd/fn/v2"
5
)
6

7
// updateLog is an append-only log that stores updates to a node's commitment
8
// chain. This structure can be seen as the "mempool" within Lightning where
9
// changes are stored before they're committed to the chain. Once an entry has
10
// been committed in both the local and remote commitment chain, then it can be
11
// removed from this log.
12
//
13
// TODO(roasbeef): create lightning package, move commitment and update to
14
// package?
15
//   - also move state machine, separate from lnwallet package
16
//   - possible embed updateLog within commitmentChain.
17
type updateLog struct {
18
        // logIndex is a monotonically increasing integer that tracks the total
19
        // number of update entries ever applied to the log. When sending new
20
        // commitment states, we include all updates up to this index.
21
        logIndex uint64
22

23
        // htlcCounter is a monotonically increasing integer that tracks the
24
        // total number of offered HTLC's by the owner of this update log,
25
        // hence the `Add` update type. We use a distinct index for this
26
        // purpose, as update's that remove entries from the log will be
27
        // indexed using this counter.
28
        htlcCounter uint64
29

30
        // List is the updatelog itself, we embed this value so updateLog has
31
        // access to all the method of a list.List.
32
        *fn.List[*paymentDescriptor]
33

34
        // updateIndex maps a `logIndex` to a particular update entry. It
35
        // deals with the four update types:
36
        //   `Fail|MalformedFail|Settle|FeeUpdate`
37
        updateIndex map[uint64]*fn.Node[*paymentDescriptor]
38

39
        // htlcIndex maps a `htlcCounter` to an offered HTLC entry, hence the
40
        // `Add` update.
41
        htlcIndex map[uint64]*fn.Node[*paymentDescriptor]
42

43
        // modifiedHtlcs is a set that keeps track of all the current modified
44
        // htlcs, hence update types `Fail|MalformedFail|Settle`. A modified
45
        // HTLC is one that's present in the log, and has as a pending fail or
46
        // settle that's attempting to consume it.
47
        modifiedHtlcs fn.Set[uint64]
48
}
49

50
// newUpdateLog creates a new updateLog instance.
51
func newUpdateLog(logIndex, htlcCounter uint64) *updateLog {
3✔
52
        return &updateLog{
3✔
53
                List:          fn.NewList[*paymentDescriptor](),
3✔
54
                updateIndex:   make(map[uint64]*fn.Node[*paymentDescriptor]),
3✔
55
                htlcIndex:     make(map[uint64]*fn.Node[*paymentDescriptor]),
3✔
56
                logIndex:      logIndex,
3✔
57
                htlcCounter:   htlcCounter,
3✔
58
                modifiedHtlcs: fn.NewSet[uint64](),
3✔
59
        }
3✔
60
}
3✔
61

62
// restoreHtlc will "restore" a prior HTLC to the updateLog. We say restore as
63
// this method is intended to be used when re-covering a prior commitment
64
// state. This function differs from appendHtlc in that it won't increment
65
// either of log's counters. If the HTLC is already present, then it is
66
// ignored.
67
func (u *updateLog) restoreHtlc(pd *paymentDescriptor) {
3✔
68
        if _, ok := u.htlcIndex[pd.HtlcIndex]; ok {
3✔
69
                return
×
70
        }
×
71

72
        u.htlcIndex[pd.HtlcIndex] = u.PushBack(pd)
3✔
73
}
74

75
// appendUpdate appends a new update to the tip of the updateLog. The entry is
76
// also added to index accordingly.
77
func (u *updateLog) appendUpdate(pd *paymentDescriptor) {
3✔
78
        u.updateIndex[u.logIndex] = u.PushBack(pd)
3✔
79
        u.logIndex++
3✔
80
}
3✔
81

82
// restoreUpdate appends a new update to the tip of the updateLog. The entry is
83
// also added to index accordingly. This function differs from appendUpdate in
84
// that it won't increment the log index counter.
UNCOV
85
func (u *updateLog) restoreUpdate(pd *paymentDescriptor) {
×
UNCOV
86
        u.updateIndex[pd.LogIndex] = u.PushBack(pd)
×
UNCOV
87
}
×
88

89
// appendHtlc appends a new HTLC offer to the tip of the update log. The entry
90
// is also added to the offer index accordingly.
91
func (u *updateLog) appendHtlc(pd *paymentDescriptor) {
3✔
92
        u.htlcIndex[u.htlcCounter] = u.PushBack(pd)
3✔
93
        u.htlcCounter++
3✔
94

3✔
95
        u.logIndex++
3✔
96
}
3✔
97

98
// lookupHtlc attempts to look up an offered HTLC according to its offer
99
// index. If the entry isn't found, then a nil pointer is returned.
100
func (u *updateLog) lookupHtlc(i uint64) *paymentDescriptor {
3✔
101
        htlc, ok := u.htlcIndex[i]
3✔
102
        if !ok {
3✔
UNCOV
103
                return nil
×
UNCOV
104
        }
×
105

106
        return htlc.Value
3✔
107
}
108

109
// remove attempts to remove an entry from the update log. If the entry is
110
// found, then the entry will be removed from the update log and index.
111
func (u *updateLog) removeUpdate(i uint64) {
3✔
112
        entry := u.updateIndex[i]
3✔
113
        u.Remove(entry)
3✔
114
        delete(u.updateIndex, i)
3✔
115
}
3✔
116

117
// removeHtlc attempts to remove an HTLC offer form the update log. If the
118
// entry is found, then the entry will be removed from both the main log and
119
// the offer index.
120
func (u *updateLog) removeHtlc(i uint64) {
3✔
121
        entry := u.htlcIndex[i]
3✔
122
        u.Remove(entry)
3✔
123
        delete(u.htlcIndex, i)
3✔
124

3✔
125
        u.modifiedHtlcs.Remove(i)
3✔
126
}
3✔
127

128
// htlcHasModification returns true if the HTLC identified by the passed index
129
// has a pending modification within the log.
130
func (u *updateLog) htlcHasModification(i uint64) bool {
3✔
131
        return u.modifiedHtlcs.Contains(i)
3✔
132
}
3✔
133

134
// markHtlcModified marks an HTLC as modified based on its HTLC index. After a
135
// call to this method, htlcHasModification will return true until the HTLC is
136
// removed.
137
func (u *updateLog) markHtlcModified(i uint64) {
3✔
138
        u.modifiedHtlcs.Add(i)
3✔
139
}
3✔
140

141
// compactLogs performs garbage collection within the log removing HTLCs which
142
// have been removed from the point-of-view of the tail of both chains. The
143
// entries which timeout/settle HTLCs are also removed.
144
func compactLogs(ourLog, theirLog *updateLog,
145
        localChainTail, remoteChainTail uint64) {
3✔
146

3✔
147
        compactLog := func(logA, logB *updateLog) {
6✔
148
                var nextA *fn.Node[*paymentDescriptor]
3✔
149
                for e := logA.Front(); e != nil; e = nextA {
6✔
150
                        // Assign next iteration element at top of loop because
3✔
151
                        // we may remove the current element from the list,
3✔
152
                        // which can change the iterated sequence.
3✔
153
                        nextA = e.Next()
3✔
154

3✔
155
                        htlc := e.Value
3✔
156
                        rmvHeights := htlc.removeCommitHeights
3✔
157

3✔
158
                        // We skip Adds, as they will be removed along with the
3✔
159
                        // fail/settles below.
3✔
160
                        if htlc.EntryType == Add {
6✔
161
                                continue
3✔
162
                        }
163

164
                        // If the HTLC hasn't yet been removed from either
165
                        // chain, the skip it.
166
                        if rmvHeights.Remote == 0 || rmvHeights.Local == 0 {
6✔
167
                                continue
3✔
168
                        }
169

170
                        // Otherwise if the height of the tail of both chains
171
                        // is at least the height in which the HTLC was
172
                        // removed, then evict the settle/timeout entry along
173
                        // with the original add entry.
174
                        if remoteChainTail >= rmvHeights.Remote &&
3✔
175
                                localChainTail >= rmvHeights.Local {
6✔
176

3✔
177
                                // Fee updates have no parent htlcs, so we only
3✔
178
                                // remove the update itself.
3✔
179
                                if htlc.EntryType == FeeUpdate {
3✔
UNCOV
180
                                        logA.removeUpdate(htlc.LogIndex)
×
UNCOV
181
                                        continue
×
182
                                }
183

184
                                // The other types (fail/settle) do have a
185
                                // parent HTLC, so we'll remove that HTLC from
186
                                // the other log.
187
                                logA.removeUpdate(htlc.LogIndex)
3✔
188
                                logB.removeHtlc(htlc.ParentIndex)
3✔
189
                        }
190
                }
191
        }
192

193
        compactLog(ourLog, theirLog)
3✔
194
        compactLog(theirLog, ourLog)
3✔
195
}
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