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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 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