Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/release-notes/release-notes-0.21.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
or key) existed. The manager now correctly regenerates both files when either
is missing, preventing "file not found" errors on startup.

- [Fixed race conditions](https://github.com/lightningnetwork/lnd/pull/XXXXX) in
the channel graph database. The `Node.PubKey()` and
`ChannelEdgeInfo.NodeKey1/NodeKey2()` methods had check-then-act races when
caching parsed public keys. Additionally, `DisconnectBlockAtHeight` was
accessing the reject and channel caches without proper locking. The caching
has been removed from the public key parsing methods, and proper mutex
protection has been added to the cache access in `DisconnectBlockAtHeight`.

# New Features

- Basic Support for [onion messaging forwarding](https://github.com/lightningnetwork/lnd/pull/9868)
Expand Down
32 changes: 2 additions & 30 deletions graph/db/models/channel_edge_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ type ChannelEdgeInfo struct {

// NodeKey1Bytes is the raw public key of the first node.
NodeKey1Bytes [33]byte
nodeKey1 *btcec.PublicKey
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

caching came from #706, I think it would be good to do a pathfinding benchmark and/or profile

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool yeah the other option is just to only store the *btcec.PublicKey and not the bytes here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other option is just to leave the caching but use a sync.Once or something

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see any pathfinding or graph loading performance degradation. I'm not sure about allocations, but haven't spotted anything suspicious and the call sites seem not to be relevant for pathfinding at least.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also checked allocations and couldn't see a difference


// NodeKey2Bytes is the raw public key of the first node.
NodeKey2Bytes [33]byte
nodeKey2 *btcec.PublicKey

// BitcoinKey1Bytes is the raw public key of the first node.
BitcoinKey1Bytes [33]byte
Expand Down Expand Up @@ -78,42 +76,16 @@ type ChannelEdgeInfo struct {
// the creation of this channel. A node is considered "first" if the
// lexicographical ordering the its serialized public key is "smaller" than
// that of the other node involved in channel creation.
//
// NOTE: By having this method to access an attribute, we ensure we only need
// to fully deserialize the pubkey if absolutely necessary.
func (c *ChannelEdgeInfo) NodeKey1() (*btcec.PublicKey, error) {
if c.nodeKey1 != nil {
return c.nodeKey1, nil
}

key, err := btcec.ParsePubKey(c.NodeKey1Bytes[:])
if err != nil {
return nil, err
}
c.nodeKey1 = key

return key, nil
return btcec.ParsePubKey(c.NodeKey1Bytes[:])
}

// NodeKey2 is the identity public key of the "second" node that was involved in
// the creation of this channel. A node is considered "second" if the
// lexicographical ordering the its serialized public key is "larger" than that
// of the other node involved in channel creation.
//
// NOTE: By having this method to access an attribute, we ensure we only need
// to fully deserialize the pubkey if absolutely necessary.
func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) {
if c.nodeKey2 != nil {
return c.nodeKey2, nil
}

key, err := btcec.ParsePubKey(c.NodeKey2Bytes[:])
if err != nil {
return nil, err
}
c.nodeKey2 = key

return key, nil
return btcec.ParsePubKey(c.NodeKey2Bytes[:])
}

// OtherNodeKeyBytes returns the node key bytes of the other end of the channel.
Expand Down
16 changes: 1 addition & 15 deletions graph/db/models/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ type Node struct {

// PubKeyBytes is the raw bytes of the public key of the target node.
PubKeyBytes [33]byte
pubKey *btcec.PublicKey

// LastUpdate is the last time the vertex information for this node has
// been updated.
Expand Down Expand Up @@ -129,21 +128,8 @@ func (n *Node) HaveAnnouncement() bool {

// PubKey is the node's long-term identity public key. This key will be used to
// authenticated any advertisements/updates sent by the node.
//
// NOTE: By having this method to access an attribute, we ensure we only need
// to fully deserialize the pubkey if absolutely necessary.
func (n *Node) PubKey() (*btcec.PublicKey, error) {
if n.pubKey != nil {
return n.pubKey, nil
}

key, err := btcec.ParsePubKey(n.PubKeyBytes[:])
if err != nil {
return nil, err
}
n.pubKey = key

return key, nil
return btcec.ParsePubKey(n.PubKeyBytes[:])
}

// NodeAnnouncement retrieves the latest node announcement of the node.
Expand Down
2 changes: 2 additions & 0 deletions graph/db/sql_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -2911,10 +2911,12 @@ func (s *SQLStore) DisconnectBlockAtHeight(height uint32) (
"height: %w", err)
}

s.cacheMu.Lock()
for _, channel := range removedChans {
s.rejectCache.remove(channel.ChannelID)
s.chanCache.remove(channel.ChannelID)
}
s.cacheMu.Unlock()
Comment on lines +2914 to +2919

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While this is correct, using defer s.cacheMu.Unlock() right after the Lock() call is more idiomatic in Go and safer against future modifications that might add an early return path within the locked section. This would also make it consistent with the change in updateEdgeCache.


return removedChans, nil
}
Expand Down
Loading