Skip to content

Commit 0d225a1

Browse files
authored
Merge pull request #10433 from ellemouton/backport-removePubKeyCaching
backport: graph: fix various races
2 parents ec480f0 + ad87b49 commit 0d225a1

File tree

4 files changed

+29
-64
lines changed

4 files changed

+29
-64
lines changed

docs/release-notes/release-notes-0.20.1.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
or key) existed. The manager now correctly regenerates both files when either
4848
is missing, preventing "file not found" errors on startup.
4949

50+
* [Fixed race conditions](https://github.com/lightningnetwork/lnd/pull/10433) in
51+
the channel graph database. The `Node.PubKey()` and
52+
`ChannelEdgeInfo.NodeKey1/NodeKey2()` methods had check-then-act races when
53+
caching parsed public keys. Additionally, `DisconnectBlockAtHeight` was
54+
accessing the reject and channel caches without proper locking. The caching
55+
has been removed from the public key parsing methods, and proper mutex
56+
protection has been added to the cache access in `DisconnectBlockAtHeight`.
57+
5058
# New Features
5159

5260
## Functional Enhancements

graph/db/models/channel_edge_info.go

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,9 @@ type ChannelEdgeInfo struct {
3333

3434
// NodeKey1Bytes is the raw public key of the first node.
3535
NodeKey1Bytes [33]byte
36-
nodeKey1 *btcec.PublicKey
3736

3837
// NodeKey2Bytes is the raw public key of the first node.
3938
NodeKey2Bytes [33]byte
40-
nodeKey2 *btcec.PublicKey
4139

4240
// BitcoinKey1Bytes is the raw public key of the first node.
4341
BitcoinKey1Bytes [33]byte
@@ -84,10 +82,8 @@ type ChannelEdgeInfo struct {
8482
func (c *ChannelEdgeInfo) AddNodeKeys(nodeKey1, nodeKey2, bitcoinKey1,
8583
bitcoinKey2 *btcec.PublicKey) {
8684

87-
c.nodeKey1 = nodeKey1
88-
copy(c.NodeKey1Bytes[:], c.nodeKey1.SerializeCompressed())
85+
copy(c.NodeKey1Bytes[:], nodeKey1.SerializeCompressed())
8986

90-
c.nodeKey2 = nodeKey2
9187
copy(c.NodeKey2Bytes[:], nodeKey2.SerializeCompressed())
9288

9389
c.bitcoinKey1 = bitcoinKey1
@@ -101,42 +97,16 @@ func (c *ChannelEdgeInfo) AddNodeKeys(nodeKey1, nodeKey2, bitcoinKey1,
10197
// the creation of this channel. A node is considered "first" if the
10298
// lexicographical ordering the its serialized public key is "smaller" than
10399
// that of the other node involved in channel creation.
104-
//
105-
// NOTE: By having this method to access an attribute, we ensure we only need
106-
// to fully deserialize the pubkey if absolutely necessary.
107100
func (c *ChannelEdgeInfo) NodeKey1() (*btcec.PublicKey, error) {
108-
if c.nodeKey1 != nil {
109-
return c.nodeKey1, nil
110-
}
111-
112-
key, err := btcec.ParsePubKey(c.NodeKey1Bytes[:])
113-
if err != nil {
114-
return nil, err
115-
}
116-
c.nodeKey1 = key
117-
118-
return key, nil
101+
return btcec.ParsePubKey(c.NodeKey1Bytes[:])
119102
}
120103

121104
// NodeKey2 is the identity public key of the "second" node that was involved in
122105
// the creation of this channel. A node is considered "second" if the
123106
// lexicographical ordering the its serialized public key is "larger" than that
124107
// of the other node involved in channel creation.
125-
//
126-
// NOTE: By having this method to access an attribute, we ensure we only need
127-
// to fully deserialize the pubkey if absolutely necessary.
128108
func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) {
129-
if c.nodeKey2 != nil {
130-
return c.nodeKey2, nil
131-
}
132-
133-
key, err := btcec.ParsePubKey(c.NodeKey2Bytes[:])
134-
if err != nil {
135-
return nil, err
136-
}
137-
c.nodeKey2 = key
138-
139-
return key, nil
109+
return btcec.ParsePubKey(c.NodeKey2Bytes[:])
140110
}
141111

142112
// BitcoinKey1 is the Bitcoin multi-sig key belonging to the first node, that

graph/db/models/node.go

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
type Node struct {
1919
// PubKeyBytes is the raw bytes of the public key of the target node.
2020
PubKeyBytes [33]byte
21-
pubKey *btcec.PublicKey
2221

2322
// HaveNodeAnnouncement indicates whether we received a node
2423
// announcement for this particular node. If true, the remaining fields
@@ -62,67 +61,53 @@ type Node struct {
6261

6362
// PubKey is the node's long-term identity public key. This key will be used to
6463
// authenticated any advertisements/updates sent by the node.
65-
//
66-
// NOTE: By having this method to access an attribute, we ensure we only need
67-
// to fully deserialize the pubkey if absolutely necessary.
68-
func (l *Node) PubKey() (*btcec.PublicKey, error) {
69-
if l.pubKey != nil {
70-
return l.pubKey, nil
71-
}
72-
73-
key, err := btcec.ParsePubKey(l.PubKeyBytes[:])
74-
if err != nil {
75-
return nil, err
76-
}
77-
l.pubKey = key
78-
79-
return key, nil
64+
func (n *Node) PubKey() (*btcec.PublicKey, error) {
65+
return btcec.ParsePubKey(n.PubKeyBytes[:])
8066
}
8167

8268
// AuthSig is a signature under the advertised public key which serves to
8369
// authenticate the attributes announced by this node.
8470
//
8571
// NOTE: By having this method to access an attribute, we ensure we only need
8672
// to fully deserialize the signature if absolutely necessary.
87-
func (l *Node) AuthSig() (*ecdsa.Signature, error) {
88-
return ecdsa.ParseSignature(l.AuthSigBytes)
73+
func (n *Node) AuthSig() (*ecdsa.Signature, error) {
74+
return ecdsa.ParseSignature(n.AuthSigBytes)
8975
}
9076

9177
// AddPubKey is a setter-link method that can be used to swap out the public
9278
// key for a node.
93-
func (l *Node) AddPubKey(key *btcec.PublicKey) {
94-
l.pubKey = key
95-
copy(l.PubKeyBytes[:], key.SerializeCompressed())
79+
func (n *Node) AddPubKey(key *btcec.PublicKey) {
80+
copy(n.PubKeyBytes[:], key.SerializeCompressed())
9681
}
9782

9883
// NodeAnnouncement retrieves the latest node announcement of the node.
99-
func (l *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
84+
func (n *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
10085
error) {
10186

102-
if !l.HaveNodeAnnouncement {
87+
if !n.HaveNodeAnnouncement {
10388
return nil, fmt.Errorf("node does not have node announcement")
10489
}
10590

106-
alias, err := lnwire.NewNodeAlias(l.Alias)
91+
alias, err := lnwire.NewNodeAlias(n.Alias)
10792
if err != nil {
10893
return nil, err
10994
}
11095

11196
nodeAnn := &lnwire.NodeAnnouncement1{
112-
Features: l.Features.RawFeatureVector,
113-
NodeID: l.PubKeyBytes,
114-
RGBColor: l.Color,
97+
Features: n.Features.RawFeatureVector,
98+
NodeID: n.PubKeyBytes,
99+
RGBColor: n.Color,
115100
Alias: alias,
116-
Addresses: l.Addresses,
117-
Timestamp: uint32(l.LastUpdate.Unix()),
118-
ExtraOpaqueData: l.ExtraOpaqueData,
101+
Addresses: n.Addresses,
102+
Timestamp: uint32(n.LastUpdate.Unix()),
103+
ExtraOpaqueData: n.ExtraOpaqueData,
119104
}
120105

121106
if !signed {
122107
return nodeAnn, nil
123108
}
124109

125-
sig, err := lnwire.NewSigFromECDSARawSignature(l.AuthSigBytes)
110+
sig, err := lnwire.NewSigFromECDSARawSignature(n.AuthSigBytes)
126111
if err != nil {
127112
return nil, err
128113
}

graph/db/sql_store.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2916,10 +2916,12 @@ func (s *SQLStore) DisconnectBlockAtHeight(height uint32) (
29162916
"height: %w", err)
29172917
}
29182918

2919+
s.cacheMu.Lock()
29192920
for _, channel := range removedChans {
29202921
s.rejectCache.remove(channel.ChannelID)
29212922
s.chanCache.remove(channel.ChannelID)
29222923
}
2924+
s.cacheMu.Unlock()
29232925

29242926
return removedChans, nil
29252927
}

0 commit comments

Comments
 (0)