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

lightningnetwork / lnd / 19699489477

26 Nov 2025 09:50AM UTC coverage: 65.173%. First build
19699489477

Pull #10379

github

web-flow
Merge 88a1a8566 into a5f300683
Pull Request #10379: [g175:3] graph/db: continue prepping `models` for V2 data

245 of 435 new or added lines in 11 files covered. (56.32%)

137805 of 211444 relevant lines covered (65.17%)

20804.44 hits per line

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

75.64
/graph/db/models/channel_edge_info.go
1
package models
2

3
import (
4
        "bytes"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/btcutil"
9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/btcsuite/btcd/wire"
11
        "github.com/lightningnetwork/lnd/fn/v2"
12
        "github.com/lightningnetwork/lnd/input"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        "github.com/lightningnetwork/lnd/routing/route"
15
)
16

17
// ChannelEdgeInfo represents a fully authenticated channel along with all its
18
// unique attributes. Once an authenticated channel announcement has been
19
// processed on the network, then an instance of ChannelEdgeInfo encapsulating
20
// the channels attributes is stored. The other portions relevant to routing
21
// policy of a channel are stored within a ChannelEdgePolicy for each direction
22
// of the channel.
23
type ChannelEdgeInfo struct {
24
        // Version is the gossip version that this channel was advertised on.
25
        Version lnwire.GossipVersion
26

27
        // ChannelID is the unique channel ID for the channel. The first 3
28
        // bytes are the block height, the next 3 the index within the block,
29
        // and the last 2 bytes are the output index for the channel.
30
        ChannelID uint64
31

32
        // ChainHash is the hash that uniquely identifies the chain that this
33
        // channel was opened within.
34
        ChainHash chainhash.Hash
35

36
        // NodeKey1Bytes is the raw public key of the first node.
37
        NodeKey1Bytes route.Vertex
38
        nodeKey1      *btcec.PublicKey
39

40
        // NodeKey2Bytes is the raw public key of the first node.
41
        NodeKey2Bytes route.Vertex
42
        nodeKey2      *btcec.PublicKey
43

44
        // BitcoinKey1Bytes is the raw public key of the first node.
45
        BitcoinKey1Bytes fn.Option[route.Vertex]
46

47
        // BitcoinKey2Bytes is the raw public key of the first node.
48
        BitcoinKey2Bytes fn.Option[route.Vertex]
49

50
        // Features is the list of protocol features supported by this channel
51
        // edge.
52
        Features *lnwire.FeatureVector
53

54
        // AuthProof is the authentication proof for this channel. This proof
55
        // contains a set of signatures binding four identities, which attests
56
        // to the legitimacy of the advertised channel.
57
        AuthProof *ChannelAuthProof
58

59
        // ChannelPoint is the funding outpoint of the channel. This can be
60
        // used to uniquely identify the channel within the channel graph.
61
        ChannelPoint wire.OutPoint
62

63
        // Capacity is the total capacity of the channel, this is determined by
64
        // the value output in the outpoint that created this channel.
65
        Capacity btcutil.Amount
66

67
        // FundingScript holds the script of the channel's funding transaction.
68
        //
69
        // NOTE: this is not currently persisted and so will not be present if
70
        // the edge object is loaded from the database.
71
        FundingScript fn.Option[[]byte]
72

73
        // ExtraOpaqueData is the set of data that was appended to this
74
        // message, some of which we may not actually know how to iterate or
75
        // parse. By holding onto this data, we ensure that we're able to
76
        // properly validate the set of signatures that cover these new fields,
77
        // and ensure we're able to make upgrades to the network in a forwards
78
        // compatible manner.
79
        ExtraOpaqueData []byte
80
}
81

82
// EdgeModifier is a functional option that modifies a ChannelEdgeInfo.
83
type EdgeModifier func(*ChannelEdgeInfo)
84

85
// WithChannelPoint sets the channel point (funding outpoint) on the edge.
86
func WithChannelPoint(cp wire.OutPoint) EdgeModifier {
1,920✔
87
        return func(e *ChannelEdgeInfo) {
3,840✔
88
                e.ChannelPoint = cp
1,920✔
89
        }
1,920✔
90
}
91

92
// WithFeatures sets the feature vector on the edge.
93
func WithFeatures(f *lnwire.RawFeatureVector) EdgeModifier {
235✔
94
        return func(e *ChannelEdgeInfo) {
470✔
95
                e.Features = lnwire.NewFeatureVector(f, lnwire.Features)
235✔
96
        }
235✔
97
}
98

99
// WithCapacity sets the capacity on the edge.
100
func WithCapacity(c btcutil.Amount) EdgeModifier {
1,811✔
101
        return func(e *ChannelEdgeInfo) {
3,622✔
102
                e.Capacity = c
1,811✔
103
        }
1,811✔
104
}
105

106
// WithChanProof sets the authentication proof on the edge.
107
func WithChanProof(proof *ChannelAuthProof) EdgeModifier {
1,966✔
108
        return func(e *ChannelEdgeInfo) {
3,932✔
109
                e.AuthProof = proof
1,966✔
110
        }
1,966✔
111
}
112

113
// ChannelV1Fields contains the fields that are specific to v1 channel
114
// announcements.
115
type ChannelV1Fields struct {
116
        // BitcoinKey1Bytes is the raw public key of the first node.
117
        BitcoinKey1Bytes route.Vertex
118

119
        // BitcoinKey2Bytes is the raw public key of the first node.
120
        BitcoinKey2Bytes route.Vertex
121

122
        // ExtraOpaqueData is the set of data that was appended to this
123
        // message, some of which we may not actually know how to iterate or
124
        // parse. By holding onto this data, we ensure that we're able to
125
        // properly validate the set of signatures that cover these new fields,
126
        // and ensure we're able to make upgrades to the network in a forwards
127
        // compatible manner.
128
        ExtraOpaqueData []byte
129
}
130

131
// NewV1Channel creates a new ChannelEdgeInfo for a v1 channel announcement.
132
// It takes the required fields for all channels (chanID, chainHash, node keys)
133
// and v1-specific fields, along with optional modifiers for setting additional
134
// fields like capacity, channel point, features, and auth proof.
135
//
136
// The constructor validates that if an AuthProof is provided via modifiers, its
137
// version matches the channel version (v1).
138
func NewV1Channel(chanID uint64, chainHash chainhash.Hash, node1,
139
        node2 route.Vertex, v1Fields *ChannelV1Fields,
140
        opts ...EdgeModifier) (*ChannelEdgeInfo, error) {
2,748✔
141

2,748✔
142
        edge := &ChannelEdgeInfo{
2,748✔
143
                Version:          lnwire.GossipVersion1,
2,748✔
144
                NodeKey1Bytes:    node1,
2,748✔
145
                NodeKey2Bytes:    node2,
2,748✔
146
                BitcoinKey1Bytes: fn.Some(v1Fields.BitcoinKey1Bytes),
2,748✔
147
                BitcoinKey2Bytes: fn.Some(v1Fields.BitcoinKey2Bytes),
2,748✔
148
                ChannelID:        chanID,
2,748✔
149
                ChainHash:        chainHash,
2,748✔
150
                Features:         lnwire.EmptyFeatureVector(),
2,748✔
151
                ExtraOpaqueData:  v1Fields.ExtraOpaqueData,
2,748✔
152
        }
2,748✔
153

2,748✔
154
        for _, opt := range opts {
8,677✔
155
                opt(edge)
5,929✔
156
        }
5,929✔
157

158
        // Validate some fields after the options have been applied.
159
        if edge.AuthProof != nil && edge.AuthProof.Version != edge.Version {
2,748✔
NEW
160
                return nil, fmt.Errorf("channel auth proof version %d does "+
×
NEW
161
                        "not match channel version %d", edge.AuthProof.Version,
×
NEW
162
                        edge.Version)
×
NEW
163
        }
×
164

165
        return edge, nil
2,748✔
166
}
167

168
// NodeKey1 is the identity public key of the "first" node that was involved in
169
// the creation of this channel. A node is considered "first" if the
170
// lexicographical ordering the its serialized public key is "smaller" than
171
// that of the other node involved in channel creation.
172
//
173
// NOTE: By having this method to access an attribute, we ensure we only need
174
// to fully deserialize the pubkey if absolutely necessary.
175
func (c *ChannelEdgeInfo) NodeKey1() (*btcec.PublicKey, error) {
3,242✔
176
        if c.nodeKey1 != nil {
3,383✔
177
                return c.nodeKey1, nil
141✔
178
        }
141✔
179

180
        key, err := btcec.ParsePubKey(c.NodeKey1Bytes[:])
3,104✔
181
        if err != nil {
3,104✔
182
                return nil, err
×
183
        }
×
184
        c.nodeKey1 = key
3,104✔
185

3,104✔
186
        return key, nil
3,104✔
187
}
188

189
// NodeKey2 is the identity public key of the "second" node that was involved in
190
// the creation of this channel. A node is considered "second" if the
191
// lexicographical ordering the its serialized public key is "larger" than that
192
// of the other node involved in channel creation.
193
//
194
// NOTE: By having this method to access an attribute, we ensure we only need
195
// to fully deserialize the pubkey if absolutely necessary.
196
func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) {
3,048✔
197
        if c.nodeKey2 != nil {
3,110✔
198
                return c.nodeKey2, nil
62✔
199
        }
62✔
200

201
        key, err := btcec.ParsePubKey(c.NodeKey2Bytes[:])
2,989✔
202
        if err != nil {
2,989✔
203
                return nil, err
×
204
        }
×
205
        c.nodeKey2 = key
2,989✔
206

2,989✔
207
        return key, nil
2,989✔
208
}
209

210
// OtherNodeKeyBytes returns the node key bytes of the other end of the channel.
211
func (c *ChannelEdgeInfo) OtherNodeKeyBytes(thisNodeKey []byte) (
212
        route.Vertex, error) {
3,845✔
213

3,845✔
214
        switch {
3,845✔
215
        case bytes.Equal(c.NodeKey1Bytes[:], thisNodeKey):
1,918✔
216
                return c.NodeKey2Bytes, nil
1,918✔
217
        case bytes.Equal(c.NodeKey2Bytes[:], thisNodeKey):
1,930✔
218
                return c.NodeKey1Bytes, nil
1,930✔
219
        default:
×
NEW
220
                return route.Vertex{}, fmt.Errorf("node not participating in " +
×
221
                        "this channel")
×
222
        }
223
}
224

225
// FundingPKScript returns the funding output's pkScript for the channel.
226
func (c *ChannelEdgeInfo) FundingPKScript() ([]byte, error) {
49✔
227
        switch c.Version {
49✔
228
        case lnwire.GossipVersion1:
49✔
229
                btc1Key, err := c.BitcoinKey1Bytes.UnwrapOrErr(
49✔
230
                        fmt.Errorf("missing bitcoin key 1"),
49✔
231
                )
49✔
232
                if err != nil {
49✔
NEW
233
                        return nil, err
×
NEW
234
                }
×
235
                btc2Key, err := c.BitcoinKey2Bytes.UnwrapOrErr(
49✔
236
                        fmt.Errorf("missing bitcoin key 2"),
49✔
237
                )
49✔
238
                if err != nil {
49✔
NEW
239
                        return nil, err
×
NEW
240
                }
×
241

242
                witnessScript, err := input.GenMultiSigScript(
49✔
243
                        btc1Key[:], btc2Key[:],
49✔
244
                )
49✔
245
                if err != nil {
49✔
NEW
246
                        return nil, err
×
NEW
247
                }
×
248

249
                return input.WitnessScriptHash(witnessScript)
49✔
250

NEW
251
        default:
×
NEW
252
                return nil, fmt.Errorf("unsupported channel version: %d",
×
NEW
253
                        c.Version)
×
254
        }
255
}
256

257
// ToChannelAnnouncement converts the ChannelEdgeInfo to a
258
// lnwire.ChannelAnnouncement1 message. Returns an error if AuthProof is nil
259
// or if the version is not v1.
260
func (c *ChannelEdgeInfo) ToChannelAnnouncement() (
261
        *lnwire.ChannelAnnouncement1, error) {
18✔
262

18✔
263
        // We currently only support v1 channel announcements.
18✔
264
        if c.Version != lnwire.GossipVersion1 {
18✔
NEW
265
                return nil, fmt.Errorf("unsupported channel version: %d",
×
NEW
266
                        c.Version)
×
NEW
267
        }
×
268

269
        // If there's no auth proof, we can't create a full channel
270
        // announcement.
271
        if c.AuthProof == nil {
18✔
NEW
272
                return nil, fmt.Errorf("cannot create channel announcement " +
×
NEW
273
                        "without auth proof")
×
NEW
274
        }
×
275

276
        btc1, err := c.BitcoinKey1Bytes.UnwrapOrErr(
18✔
277
                fmt.Errorf("bitcoin key 1 missing for v1 channel announcement"),
18✔
278
        )
18✔
279
        if err != nil {
18✔
NEW
280
                return nil, err
×
NEW
281
        }
×
282

283
        btc2, err := c.BitcoinKey2Bytes.UnwrapOrErr(
18✔
284
                fmt.Errorf("bitcoin key 2 missing for v1 channel announcement"),
18✔
285
        )
18✔
286
        if err != nil {
18✔
NEW
287
                return nil, err
×
NEW
288
        }
×
289

290
        chanID := lnwire.NewShortChanIDFromInt(c.ChannelID)
18✔
291
        chanAnn := &lnwire.ChannelAnnouncement1{
18✔
292
                ShortChannelID:  chanID,
18✔
293
                NodeID1:         c.NodeKey1Bytes,
18✔
294
                NodeID2:         c.NodeKey2Bytes,
18✔
295
                ChainHash:       c.ChainHash,
18✔
296
                BitcoinKey1:     btc1,
18✔
297
                BitcoinKey2:     btc2,
18✔
298
                Features:        c.Features.RawFeatureVector,
18✔
299
                ExtraOpaqueData: c.ExtraOpaqueData,
18✔
300
        }
18✔
301

18✔
302
        chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
18✔
303
                c.AuthProof.NodeSig1(),
18✔
304
        )
18✔
305
        if err != nil {
18✔
NEW
306
                return nil, err
×
NEW
307
        }
×
308

309
        chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
18✔
310
                c.AuthProof.NodeSig2(),
18✔
311
        )
18✔
312
        if err != nil {
18✔
NEW
313
                return nil, err
×
NEW
314
        }
×
315

316
        chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
18✔
317
                c.AuthProof.BitcoinSig1(),
18✔
318
        )
18✔
319
        if err != nil {
18✔
NEW
320
                return nil, err
×
NEW
321
        }
×
322

323
        chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
18✔
324
                c.AuthProof.BitcoinSig2(),
18✔
325
        )
18✔
326
        if err != nil {
18✔
NEW
327
                return nil, err
×
NEW
328
        }
×
329

330
        return chanAnn, nil
18✔
331
}
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