• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
You are now the owner of this repo.

lightningnetwork / lnd / 22219978692

20 Feb 2026 10:10AM UTC coverage: 64.879% (-0.2%) from 65.086%
22219978692

Pull #10582

github

web-flow
Merge 68afcb574 into 78b2036f3
Pull Request #10582: [g175] graph/db: add versioned range queries and complete v2 graph query migration

649 of 1769 new or added lines in 24 files covered. (36.69%)

100 existing lines in 22 files now uncovered.

139525 of 215053 relevant lines covered (64.88%)

20407.53 hits per line

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

57.58
/graph/db/models/node.go
1
package models
2

3
import (
4
        "fmt"
5
        "image/color"
6
        "net"
7
        "time"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/lightningnetwork/lnd/fn/v2"
11
        "github.com/lightningnetwork/lnd/lnwire"
12
        "github.com/lightningnetwork/lnd/routing/route"
13
)
14

15
// Node represents an individual vertex/node within the channel graph.
16
// A node is connected to other nodes by one or more channel edges emanating
17
// from it. As the graph is directed, a node will also have an incoming edge
18
// attached to it for each outgoing edge.
19
type Node struct {
20
        // Version is the gossip version that this node was advertised on.
21
        Version lnwire.GossipVersion
22

23
        // PubKeyBytes is the raw bytes of the public key of the target node.
24
        PubKeyBytes [33]byte
25

26
        // LastUpdate is the last time the vertex information for this node has
27
        // been updated.
28
        LastUpdate time.Time
29

30
        // LastBlockHeight is the block height that timestamps the last update
31
        // we received for this node. This is only used if this is a V2 node
32
        // announcement.
33
        LastBlockHeight uint32
34

35
        // Address is the TCP address this node is reachable over.
36
        Addresses []net.Addr
37

38
        // Color is the selected color for the node.
39
        Color fn.Option[color.RGBA]
40

41
        // Alias is a nick-name for the node. The alias can be used to confirm
42
        // a node's identity or to serve as a short ID for an address book.
43
        Alias fn.Option[string]
44

45
        // AuthSigBytes is the raw signature under the advertised public key
46
        // which serves to authenticate the attributes announced by this node.
47
        AuthSigBytes []byte
48

49
        // Features is the list of protocol features supported by this node.
50
        Features *lnwire.FeatureVector
51

52
        // ExtraOpaqueData is the set of data that was appended to this
53
        // message, some of which we may not actually know how to iterate or
54
        // parse. By holding onto this data, we ensure that we're able to
55
        // properly validate the set of signatures that cover these new fields,
56
        // and ensure we're able to make upgrades to the network in a forwards
57
        // compatible manner. This is only used for V1 node announcements.
58
        ExtraOpaqueData []byte
59

60
        // ExtraSignedFields is a map of extra fields that are covered by the
61
        // node announcement's signature that we have not explicitly parsed.
62
        // This is only used for version 2 node announcements and beyond.
63
        ExtraSignedFields map[uint64][]byte
64
}
65

66
// NodeV1Fields houses the fields that are specific to a version 1 node
67
// announcement.
68
type NodeV1Fields struct {
69
        // Address is the TCP address this node is reachable over.
70
        Addresses []net.Addr
71

72
        // AuthSigBytes is the raw signature under the advertised public key
73
        // which serves to authenticate the attributes announced by this node.
74
        AuthSigBytes []byte
75

76
        // Features is the list of protocol features supported by this node.
77
        Features *lnwire.RawFeatureVector
78

79
        // Color is the selected color for the node.
80
        Color color.RGBA
81

82
        // Alias is a nick-name for the node. The alias can be used to confirm
83
        // a node's identity or to serve as a short ID for an address book.
84
        Alias string
85

86
        // LastUpdate is the last time the vertex information for this node has
87
        // been updated.
88
        LastUpdate time.Time
89

90
        // ExtraOpaqueData is the set of data that was appended to this
91
        // message, some of which we may not actually know how to iterate or
92
        // parse. By holding onto this data, we ensure that we're able to
93
        // properly validate the set of signatures that cover these new fields,
94
        // and ensure we're able to make upgrades to the network in a forwards
95
        // compatible manner.
96
        ExtraOpaqueData []byte
97
}
98

99
// NewV1Node creates a new version 1 node from the passed fields.
100
func NewV1Node(pub route.Vertex, n *NodeV1Fields) *Node {
1,137✔
101
        return &Node{
1,137✔
102
                Version:      lnwire.GossipVersion1,
1,137✔
103
                PubKeyBytes:  pub,
1,137✔
104
                Addresses:    n.Addresses,
1,137✔
105
                AuthSigBytes: n.AuthSigBytes,
1,137✔
106
                Features: lnwire.NewFeatureVector(
1,137✔
107
                        n.Features, lnwire.Features,
1,137✔
108
                ),
1,137✔
109
                Color:           fn.Some(n.Color),
1,137✔
110
                Alias:           fn.Some(n.Alias),
1,137✔
111
                LastUpdate:      n.LastUpdate,
1,137✔
112
                ExtraOpaqueData: n.ExtraOpaqueData,
1,137✔
113
        }
1,137✔
114
}
1,137✔
115

116
// NodeV2Fields houses the fields that are specific to a version 2 node
117
// announcement.
118
type NodeV2Fields struct {
119
        // LastBlockHeight is the block height that timestamps the last update
120
        // we received for this node.
121
        LastBlockHeight uint32
122

123
        // Address is the TCP address this node is reachable over.
124
        Addresses []net.Addr
125

126
        // Color is the selected color for the node.
127
        Color fn.Option[color.RGBA]
128

129
        // Alias is a nick-name for the node. The alias can be used to confirm
130
        // a node's identity or to serve as a short ID for an address book.
131
        Alias fn.Option[string]
132

133
        // Signature is the schnorr signature under the advertised public key
134
        // which serves to authenticate the attributes announced by this node.
135
        Signature []byte
136

137
        // Features is the list of protocol features supported by this node.
138
        Features *lnwire.RawFeatureVector
139

140
        // ExtraSignedFields is a map of extra fields that are covered by the
141
        // node announcement's signature that we have not explicitly parsed.
142
        ExtraSignedFields map[uint64][]byte
143
}
144

145
// NewV2Node creates a new version 2 node from the passed fields.
146
func NewV2Node(pub route.Vertex, n *NodeV2Fields) *Node {
×
147
        return &Node{
×
148
                Version:      lnwire.GossipVersion2,
×
149
                PubKeyBytes:  pub,
×
150
                Addresses:    n.Addresses,
×
151
                AuthSigBytes: n.Signature,
×
152
                Features: lnwire.NewFeatureVector(
×
153
                        n.Features, lnwire.Features,
×
154
                ),
×
155
                LastBlockHeight:   n.LastBlockHeight,
×
156
                Color:             n.Color,
×
157
                Alias:             n.Alias,
×
158
                LastUpdate:        time.Unix(0, 0),
×
159
                ExtraSignedFields: n.ExtraSignedFields,
×
160
        }
×
161
}
×
162

163
// NewV1ShellNode creates a new shell version 1 node.
164
func NewV1ShellNode(pubKey route.Vertex) *Node {
9,393✔
165
        return NewShellNode(lnwire.GossipVersion1, pubKey)
9,393✔
166
}
9,393✔
167

168
// NewShellNode creates a new shell node with the given gossip version and
169
// public key.
170
func NewShellNode(v lnwire.GossipVersion, pubKey route.Vertex) *Node {
9,395✔
171
        return &Node{
9,395✔
172
                Version:     v,
9,395✔
173
                PubKeyBytes: pubKey,
9,395✔
174
                Features:    lnwire.EmptyFeatureVector(),
9,395✔
175
                LastUpdate:  time.Unix(0, 0),
9,395✔
176
        }
9,395✔
177
}
9,395✔
178

179
// HaveAnnouncement returns true if we have received a node announcement for
180
// this node. We determine this by checking if we have a signature for the
181
// announcement.
182
func (n *Node) HaveAnnouncement() bool {
1,196✔
183
        return len(n.AuthSigBytes) > 0
1,196✔
184
}
1,196✔
185

186
// PubKey is the node's long-term identity public key. This key will be used to
187
// authenticated any advertisements/updates sent by the node.
188
func (n *Node) PubKey() (*btcec.PublicKey, error) {
2,938✔
189
        return btcec.ParsePubKey(n.PubKeyBytes[:])
2,938✔
190
}
2,938✔
191

192
// NodeAnnouncement retrieves the latest node announcement of the node.
193
func (n *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
194
        error) {
18✔
195

18✔
196
        // Error out if we request the signed announcement, but we don't have
18✔
197
        // a signature for this announcement.
18✔
198
        if !n.HaveAnnouncement() && signed {
21✔
199
                return nil, fmt.Errorf("node does not have node announcement")
3✔
200
        }
3✔
201

202
        alias, err := lnwire.NewNodeAlias(n.Alias.UnwrapOr(""))
18✔
203
        if err != nil {
18✔
204
                return nil, err
×
205
        }
×
206

207
        nodeAnn := &lnwire.NodeAnnouncement1{
18✔
208
                Features:        n.Features.RawFeatureVector,
18✔
209
                NodeID:          n.PubKeyBytes,
18✔
210
                RGBColor:        n.Color.UnwrapOr(color.RGBA{}),
18✔
211
                Alias:           alias,
18✔
212
                Addresses:       n.Addresses,
18✔
213
                Timestamp:       uint32(n.LastUpdate.Unix()),
18✔
214
                ExtraOpaqueData: n.ExtraOpaqueData,
18✔
215
        }
18✔
216

18✔
217
        if !signed {
21✔
218
                return nodeAnn, nil
3✔
219
        }
3✔
220

221
        sig, err := lnwire.NewSigFromECDSARawSignature(n.AuthSigBytes)
18✔
222
        if err != nil {
18✔
223
                return nil, err
×
224
        }
×
225

226
        nodeAnn.Signature = sig
18✔
227

18✔
228
        return nodeAnn, nil
18✔
229
}
230

231
// NodeFromWireAnnouncement creates a Node instance from a node announcement
232
// wire message.
233
func NodeFromWireAnnouncement(msg lnwire.NodeAnnouncement) (*Node, error) {
20✔
234
        switch msg := msg.(type) {
20✔
235
        case *lnwire.NodeAnnouncement1:
20✔
236
                timestamp := time.Unix(int64(msg.Timestamp), 0)
20✔
237

20✔
238
                return NewV1Node(
20✔
239
                        msg.NodeID,
20✔
240
                        &NodeV1Fields{
20✔
241
                                LastUpdate:      timestamp,
20✔
242
                                Addresses:       msg.Addresses,
20✔
243
                                Alias:           msg.Alias.String(),
20✔
244
                                AuthSigBytes:    msg.Signature.ToSignatureBytes(),
20✔
245
                                Features:        msg.Features,
20✔
246
                                Color:           msg.RGBColor,
20✔
247
                                ExtraOpaqueData: msg.ExtraOpaqueData,
20✔
248
                        },
20✔
249
                ), nil
20✔
250

NEW
251
        case *lnwire.NodeAnnouncement2:
×
NEW
252
                var addrs []net.Addr
×
NEW
253
                msg.IPV4Addrs.ValOpt().WhenSome(func(ipv4Addrs lnwire.IPV4Addrs) {
×
NEW
254
                        for _, addr := range ipv4Addrs {
×
NEW
255
                                addrs = append(addrs, addr)
×
NEW
256
                        }
×
257
                })
NEW
258
                msg.IPV6Addrs.ValOpt().WhenSome(func(ipv6Addrs lnwire.IPV6Addrs) {
×
NEW
259
                        for _, addr := range ipv6Addrs {
×
NEW
260
                                addrs = append(addrs, addr)
×
NEW
261
                        }
×
262
                })
NEW
263
                msg.TorV3Addrs.ValOpt().WhenSome(func(torAddrs lnwire.TorV3Addrs) {
×
NEW
264
                        for _, addr := range torAddrs {
×
NEW
265
                                addrs = append(addrs, addr)
×
NEW
266
                        }
×
267
                })
NEW
268
                msg.DNSHostName.ValOpt().WhenSome(func(dnsAddr lnwire.DNSAddress) {
×
NEW
269
                        dns := dnsAddr
×
NEW
270
                        addrs = append(addrs, &dns)
×
NEW
271
                })
×
272

NEW
273
                nodeColor := fn.MapOption(func(c lnwire.Color) color.RGBA {
×
NEW
274
                        return color.RGBA(c)
×
NEW
275
                })(msg.Color.ValOpt())
×
276

NEW
277
                nodeAlias := fn.MapOption(func(a lnwire.NodeAlias2) string {
×
NEW
278
                        return string(a)
×
NEW
279
                })(msg.Alias.ValOpt())
×
280

NEW
281
                return NewV2Node(
×
NEW
282
                        msg.NodeID.Val, &NodeV2Fields{
×
NEW
283
                                LastBlockHeight:   msg.BlockHeight.Val,
×
NEW
284
                                Addresses:         addrs,
×
NEW
285
                                Color:             nodeColor,
×
NEW
286
                                Alias:             nodeAlias,
×
NEW
287
                                Signature:         msg.Signature.Val.ToSignatureBytes(),
×
NEW
288
                                Features:          &msg.Features.Val,
×
NEW
289
                                ExtraSignedFields: msg.ExtraSignedFields,
×
NEW
290
                        },
×
NEW
291
                ), nil
×
292
        }
293

NEW
294
        return nil, fmt.Errorf("unsupported node announcement: %T", msg)
×
295
}
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

© 2026 Coveralls, Inc