• 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

62.98
/channeldb/nodes.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "io"
6
        "net"
7
        "time"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/btcsuite/btcd/wire"
11
        graphdb "github.com/lightningnetwork/lnd/graph/db"
12
        "github.com/lightningnetwork/lnd/kvdb"
13
)
14

15
var (
16
        // nodeInfoBucket stores metadata pertaining to nodes that we've had
17
        // direct channel-based correspondence with. This bucket allows one to
18
        // query for all open channels pertaining to the node by exploring each
19
        // node's sub-bucket within the openChanBucket.
20
        nodeInfoBucket = []byte("nib")
21
)
22

23
// LinkNode stores metadata related to node's that we have/had a direct
24
// channel open with. Information such as the Bitcoin network the node
25
// advertised, and its identity public key are also stored. Additionally, this
26
// struct and the bucket its stored within have store data similar to that of
27
// Bitcoin's addrmanager. The TCP address information stored within the struct
28
// can be used to establish persistent connections will all channel
29
// counterparties on daemon startup.
30
//
31
// TODO(roasbeef): also add current OnionKey plus rotation schedule?
32
// TODO(roasbeef): add bitfield for supported services
33
//   - possibly add a wire.NetAddress type, type
34
type LinkNode struct {
35
        // Network indicates the Bitcoin network that the LinkNode advertises
36
        // for incoming channel creation.
37
        Network wire.BitcoinNet
38

39
        // IdentityPub is the node's current identity public key. Any
40
        // channel/topology related information received by this node MUST be
41
        // signed by this public key.
42
        IdentityPub *btcec.PublicKey
43

44
        // LastSeen tracks the last time this node was seen within the network.
45
        // A node should be marked as seen if the daemon either is able to
46
        // establish an outgoing connection to the node or receives a new
47
        // incoming connection from the node. This timestamp (stored in unix
48
        // epoch) may be used within a heuristic which aims to determine when a
49
        // channel should be unilaterally closed due to inactivity.
50
        //
51
        // TODO(roasbeef): replace with block hash/height?
52
        //  * possibly add a time-value metric into the heuristic?
53
        LastSeen time.Time
54

55
        // Addresses is a list of IP address in which either we were able to
56
        // reach the node over in the past, OR we received an incoming
57
        // authenticated connection for the stored identity public key.
58
        Addresses []net.Addr
59

60
        // db is the database instance this node was fetched from. This is used
61
        // to sync back the node's state if it is updated.
62
        db *LinkNodeDB
63
}
64

65
// NewLinkNode creates a new LinkNode from the provided parameters, which is
66
// backed by an instance of a link node DB.
67
func NewLinkNode(db *LinkNodeDB, bitNet wire.BitcoinNet, pub *btcec.PublicKey,
68
        addrs ...net.Addr) *LinkNode {
3✔
69

3✔
70
        return &LinkNode{
3✔
71
                Network:     bitNet,
3✔
72
                IdentityPub: pub,
3✔
73
                LastSeen:    time.Now(),
3✔
74
                Addresses:   addrs,
3✔
75
                db:          db,
3✔
76
        }
3✔
77
}
3✔
78

79
// UpdateLastSeen updates the last time this node was directly encountered on
80
// the Lightning Network.
UNCOV
81
func (l *LinkNode) UpdateLastSeen(lastSeen time.Time) error {
×
UNCOV
82
        l.LastSeen = lastSeen
×
UNCOV
83

×
UNCOV
84
        return l.Sync()
×
UNCOV
85
}
×
86

87
// AddAddress appends the specified TCP address to the list of known addresses
88
// this node is/was known to be reachable at.
UNCOV
89
func (l *LinkNode) AddAddress(addr net.Addr) error {
×
UNCOV
90
        for _, a := range l.Addresses {
×
UNCOV
91
                if a.String() == addr.String() {
×
92
                        return nil
×
93
                }
×
94
        }
95

UNCOV
96
        l.Addresses = append(l.Addresses, addr)
×
UNCOV
97

×
UNCOV
98
        return l.Sync()
×
99
}
100

101
// Sync performs a full database sync which writes the current up-to-date data
102
// within the struct to the database.
UNCOV
103
func (l *LinkNode) Sync() error {
×
UNCOV
104
        // Finally update the database by storing the link node and updating
×
UNCOV
105
        // any relevant indexes.
×
UNCOV
106
        return kvdb.Update(l.db.backend, func(tx kvdb.RwTx) error {
×
UNCOV
107
                nodeMetaBucket := tx.ReadWriteBucket(nodeInfoBucket)
×
UNCOV
108
                if nodeMetaBucket == nil {
×
109
                        return ErrLinkNodesNotFound
×
110
                }
×
111

UNCOV
112
                return putLinkNode(nodeMetaBucket, l)
×
UNCOV
113
        }, func() {})
×
114
}
115

116
// putLinkNode serializes then writes the encoded version of the passed link
117
// node into the nodeMetaBucket. This function is provided in order to allow
118
// the ability to re-use a database transaction across many operations.
119
func putLinkNode(nodeMetaBucket kvdb.RwBucket, l *LinkNode) error {
3✔
120
        // First serialize the LinkNode into its raw-bytes encoding.
3✔
121
        var b bytes.Buffer
3✔
122
        if err := serializeLinkNode(&b, l); err != nil {
3✔
123
                return err
×
124
        }
×
125

126
        // Finally insert the link-node into the node metadata bucket keyed
127
        // according to the its pubkey serialized in compressed form.
128
        nodePub := l.IdentityPub.SerializeCompressed()
3✔
129
        return nodeMetaBucket.Put(nodePub, b.Bytes())
3✔
130
}
131

132
// LinkNodeDB is a database that keeps track of all link nodes.
133
type LinkNodeDB struct {
134
        backend kvdb.Backend
135
}
136

137
// DeleteLinkNode removes the link node with the given identity from the
138
// database.
139
func (l *LinkNodeDB) DeleteLinkNode(identity *btcec.PublicKey) error {
3✔
140
        return kvdb.Update(l.backend, func(tx kvdb.RwTx) error {
6✔
141
                return deleteLinkNode(tx, identity)
3✔
142
        }, func() {})
6✔
143
}
144

145
func deleteLinkNode(tx kvdb.RwTx, identity *btcec.PublicKey) error {
3✔
146
        nodeMetaBucket := tx.ReadWriteBucket(nodeInfoBucket)
3✔
147
        if nodeMetaBucket == nil {
3✔
148
                return ErrLinkNodesNotFound
×
149
        }
×
150

151
        pubKey := identity.SerializeCompressed()
3✔
152
        return nodeMetaBucket.Delete(pubKey)
3✔
153
}
154

155
// FetchLinkNode attempts to lookup the data for a LinkNode based on a target
156
// identity public key. If a particular LinkNode for the passed identity public
157
// key cannot be found, then ErrNodeNotFound if returned.
158
func (l *LinkNodeDB) FetchLinkNode(identity *btcec.PublicKey) (*LinkNode, error) {
3✔
159
        var linkNode *LinkNode
3✔
160
        err := kvdb.View(l.backend, func(tx kvdb.RTx) error {
6✔
161
                node, err := fetchLinkNode(tx, identity)
3✔
162
                if err != nil {
3✔
UNCOV
163
                        return err
×
UNCOV
164
                }
×
165

166
                linkNode = node
3✔
167
                return nil
3✔
168
        }, func() {
3✔
169
                linkNode = nil
3✔
170
        })
3✔
171

172
        return linkNode, err
3✔
173
}
174

175
func fetchLinkNode(tx kvdb.RTx, targetPub *btcec.PublicKey) (*LinkNode, error) {
3✔
176
        // First fetch the bucket for storing node metadata, bailing out early
3✔
177
        // if it hasn't been created yet.
3✔
178
        nodeMetaBucket := tx.ReadBucket(nodeInfoBucket)
3✔
179
        if nodeMetaBucket == nil {
3✔
180
                return nil, ErrLinkNodesNotFound
×
181
        }
×
182

183
        // If a link node for that particular public key cannot be located,
184
        // then exit early with an ErrNodeNotFound.
185
        pubKey := targetPub.SerializeCompressed()
3✔
186
        nodeBytes := nodeMetaBucket.Get(pubKey)
3✔
187
        if nodeBytes == nil {
3✔
UNCOV
188
                return nil, ErrNodeNotFound
×
UNCOV
189
        }
×
190

191
        // Finally, decode and allocate a fresh LinkNode object to be returned
192
        // to the caller.
193
        nodeReader := bytes.NewReader(nodeBytes)
3✔
194
        return deserializeLinkNode(nodeReader)
3✔
195
}
196

197
// TODO(roasbeef): update link node addrs in server upon connection
198

199
// FetchAllLinkNodes starts a new database transaction to fetch all nodes with
200
// whom we have active channels with.
201
func (l *LinkNodeDB) FetchAllLinkNodes() ([]*LinkNode, error) {
3✔
202
        var linkNodes []*LinkNode
3✔
203
        err := kvdb.View(l.backend, func(tx kvdb.RTx) error {
6✔
204
                nodes, err := fetchAllLinkNodes(tx)
3✔
205
                if err != nil {
3✔
206
                        return err
×
207
                }
×
208

209
                linkNodes = nodes
3✔
210
                return nil
3✔
211
        }, func() {
3✔
212
                linkNodes = nil
3✔
213
        })
3✔
214
        if err != nil {
3✔
215
                return nil, err
×
216
        }
×
217

218
        return linkNodes, nil
3✔
219
}
220

221
// fetchAllLinkNodes uses an existing database transaction to fetch all nodes
222
// with whom we have active channels with.
223
func fetchAllLinkNodes(tx kvdb.RTx) ([]*LinkNode, error) {
3✔
224
        nodeMetaBucket := tx.ReadBucket(nodeInfoBucket)
3✔
225
        if nodeMetaBucket == nil {
3✔
226
                return nil, ErrLinkNodesNotFound
×
227
        }
×
228

229
        var linkNodes []*LinkNode
3✔
230
        err := nodeMetaBucket.ForEach(func(k, v []byte) error {
6✔
231
                if v == nil {
3✔
232
                        return nil
×
233
                }
×
234

235
                nodeReader := bytes.NewReader(v)
3✔
236
                linkNode, err := deserializeLinkNode(nodeReader)
3✔
237
                if err != nil {
3✔
238
                        return err
×
239
                }
×
240

241
                linkNodes = append(linkNodes, linkNode)
3✔
242
                return nil
3✔
243
        })
244
        if err != nil {
3✔
245
                return nil, err
×
246
        }
×
247

248
        return linkNodes, nil
3✔
249
}
250

251
func serializeLinkNode(w io.Writer, l *LinkNode) error {
3✔
252
        var buf [8]byte
3✔
253

3✔
254
        byteOrder.PutUint32(buf[:4], uint32(l.Network))
3✔
255
        if _, err := w.Write(buf[:4]); err != nil {
3✔
256
                return err
×
257
        }
×
258

259
        serializedID := l.IdentityPub.SerializeCompressed()
3✔
260
        if _, err := w.Write(serializedID); err != nil {
3✔
261
                return err
×
262
        }
×
263

264
        seenUnix := uint64(l.LastSeen.Unix())
3✔
265
        byteOrder.PutUint64(buf[:], seenUnix)
3✔
266
        if _, err := w.Write(buf[:]); err != nil {
3✔
267
                return err
×
268
        }
×
269

270
        numAddrs := uint32(len(l.Addresses))
3✔
271
        byteOrder.PutUint32(buf[:4], numAddrs)
3✔
272
        if _, err := w.Write(buf[:4]); err != nil {
3✔
273
                return err
×
274
        }
×
275

276
        for _, addr := range l.Addresses {
6✔
277
                if err := graphdb.SerializeAddr(w, addr); err != nil {
3✔
278
                        return err
×
279
                }
×
280
        }
281

282
        return nil
3✔
283
}
284

285
func deserializeLinkNode(r io.Reader) (*LinkNode, error) {
3✔
286
        var (
3✔
287
                err error
3✔
288
                buf [8]byte
3✔
289
        )
3✔
290

3✔
291
        node := &LinkNode{}
3✔
292

3✔
293
        if _, err := io.ReadFull(r, buf[:4]); err != nil {
3✔
294
                return nil, err
×
295
        }
×
296
        node.Network = wire.BitcoinNet(byteOrder.Uint32(buf[:4]))
3✔
297

3✔
298
        var pub [33]byte
3✔
299
        if _, err := io.ReadFull(r, pub[:]); err != nil {
3✔
300
                return nil, err
×
301
        }
×
302
        node.IdentityPub, err = btcec.ParsePubKey(pub[:])
3✔
303
        if err != nil {
3✔
304
                return nil, err
×
305
        }
×
306

307
        if _, err := io.ReadFull(r, buf[:]); err != nil {
3✔
308
                return nil, err
×
309
        }
×
310
        node.LastSeen = time.Unix(int64(byteOrder.Uint64(buf[:])), 0)
3✔
311

3✔
312
        if _, err := io.ReadFull(r, buf[:4]); err != nil {
3✔
313
                return nil, err
×
314
        }
×
315
        numAddrs := byteOrder.Uint32(buf[:4])
3✔
316

3✔
317
        node.Addresses = make([]net.Addr, numAddrs)
3✔
318
        for i := uint32(0); i < numAddrs; i++ {
6✔
319
                addr, err := graphdb.DeserializeAddr(r)
3✔
320
                if err != nil {
3✔
321
                        return nil, err
×
322
                }
×
323
                node.Addresses[i] = addr
3✔
324
        }
325

326
        return node, nil
3✔
327
}
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