lnd/graph/db/models/node.go
Elle Mouton 3dfc835354
graph/db: add V2 Node constructor
We add a new NewV2Node constructor which takes a new NodeV2Fields as a
parameter. This NodeV2Fields struct defines the fields that can be set
in a models.Node if the version is V2.
2026-02-13 07:21:20 +02:00

248 lines
7.8 KiB
Go

package models
import (
"fmt"
"image/color"
"net"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
// Node represents an individual vertex/node within the channel graph.
// A node is connected to other nodes by one or more channel edges emanating
// from it. As the graph is directed, a node will also have an incoming edge
// attached to it for each outgoing edge.
type Node struct {
// Version is the gossip version that this node was advertised on.
Version lnwire.GossipVersion
// PubKeyBytes is the raw bytes of the public key of the target node.
PubKeyBytes [33]byte
// LastUpdate is the last time the vertex information for this node has
// been updated.
LastUpdate time.Time
// LastBlockHeight is the block height that timestamps the last update
// we received for this node. This is only used if this is a V2 node
// announcement.
LastBlockHeight uint32
// Address is the TCP address this node is reachable over.
Addresses []net.Addr
// Color is the selected color for the node.
Color fn.Option[color.RGBA]
// Alias is a nick-name for the node. The alias can be used to confirm
// a node's identity or to serve as a short ID for an address book.
Alias fn.Option[string]
// AuthSigBytes is the raw signature under the advertised public key
// which serves to authenticate the attributes announced by this node.
AuthSigBytes []byte
// Features is the list of protocol features supported by this node.
Features *lnwire.FeatureVector
// ExtraOpaqueData is the set of data that was appended to this
// message, some of which we may not actually know how to iterate or
// parse. By holding onto this data, we ensure that we're able to
// properly validate the set of signatures that cover these new fields,
// and ensure we're able to make upgrades to the network in a forwards
// compatible manner. This is only used for V1 node announcements.
ExtraOpaqueData []byte
// ExtraSignedFields is a map of extra fields that are covered by the
// node announcement's signature that we have not explicitly parsed.
// This is only used for version 2 node announcements and beyond.
ExtraSignedFields map[uint64][]byte
}
// NodeV1Fields houses the fields that are specific to a version 1 node
// announcement.
type NodeV1Fields struct {
// Address is the TCP address this node is reachable over.
Addresses []net.Addr
// AuthSigBytes is the raw signature under the advertised public key
// which serves to authenticate the attributes announced by this node.
AuthSigBytes []byte
// Features is the list of protocol features supported by this node.
Features *lnwire.RawFeatureVector
// Color is the selected color for the node.
Color color.RGBA
// Alias is a nick-name for the node. The alias can be used to confirm
// a node's identity or to serve as a short ID for an address book.
Alias string
// LastUpdate is the last time the vertex information for this node has
// been updated.
LastUpdate time.Time
// ExtraOpaqueData is the set of data that was appended to this
// message, some of which we may not actually know how to iterate or
// parse. By holding onto this data, we ensure that we're able to
// properly validate the set of signatures that cover these new fields,
// and ensure we're able to make upgrades to the network in a forwards
// compatible manner.
ExtraOpaqueData []byte
}
// NewV1Node creates a new version 1 node from the passed fields.
func NewV1Node(pub route.Vertex, n *NodeV1Fields) *Node {
return &Node{
Version: lnwire.GossipVersion1,
PubKeyBytes: pub,
Addresses: n.Addresses,
AuthSigBytes: n.AuthSigBytes,
Features: lnwire.NewFeatureVector(
n.Features, lnwire.Features,
),
Color: fn.Some(n.Color),
Alias: fn.Some(n.Alias),
LastUpdate: n.LastUpdate,
ExtraOpaqueData: n.ExtraOpaqueData,
}
}
// NodeV2Fields houses the fields that are specific to a version 2 node
// announcement.
type NodeV2Fields struct {
// LastBlockHeight is the block height that timestamps the last update
// we received for this node.
LastBlockHeight uint32
// Address is the TCP address this node is reachable over.
Addresses []net.Addr
// Color is the selected color for the node.
Color fn.Option[color.RGBA]
// Alias is a nick-name for the node. The alias can be used to confirm
// a node's identity or to serve as a short ID for an address book.
Alias fn.Option[string]
// Signature is the schnorr signature under the advertised public key
// which serves to authenticate the attributes announced by this node.
Signature []byte
// Features is the list of protocol features supported by this node.
Features *lnwire.RawFeatureVector
// ExtraSignedFields is a map of extra fields that are covered by the
// node announcement's signature that we have not explicitly parsed.
ExtraSignedFields map[uint64][]byte
}
// NewV2Node creates a new version 2 node from the passed fields.
func NewV2Node(pub route.Vertex, n *NodeV2Fields) *Node {
return &Node{
Version: lnwire.GossipVersion2,
PubKeyBytes: pub,
Addresses: n.Addresses,
AuthSigBytes: n.Signature,
Features: lnwire.NewFeatureVector(
n.Features, lnwire.Features,
),
LastBlockHeight: n.LastBlockHeight,
Color: n.Color,
Alias: n.Alias,
LastUpdate: time.Unix(0, 0),
ExtraSignedFields: n.ExtraSignedFields,
}
}
// NewV1ShellNode creates a new shell version 1 node.
func NewV1ShellNode(pubKey route.Vertex) *Node {
return NewShellNode(lnwire.GossipVersion1, pubKey)
}
// NewShellNode creates a new shell node with the given gossip version and
// public key.
func NewShellNode(v lnwire.GossipVersion, pubKey route.Vertex) *Node {
return &Node{
Version: v,
PubKeyBytes: pubKey,
Features: lnwire.EmptyFeatureVector(),
LastUpdate: time.Unix(0, 0),
}
}
// HaveAnnouncement returns true if we have received a node announcement for
// this node. We determine this by checking if we have a signature for the
// announcement.
func (n *Node) HaveAnnouncement() bool {
return len(n.AuthSigBytes) > 0
}
// PubKey is the node's long-term identity public key. This key will be used to
// authenticated any advertisements/updates sent by the node.
func (n *Node) PubKey() (*btcec.PublicKey, error) {
return btcec.ParsePubKey(n.PubKeyBytes[:])
}
// NodeAnnouncement retrieves the latest node announcement of the node.
func (n *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
error) {
// Error out if we request the signed announcement, but we don't have
// a signature for this announcement.
if !n.HaveAnnouncement() && signed {
return nil, fmt.Errorf("node does not have node announcement")
}
alias, err := lnwire.NewNodeAlias(n.Alias.UnwrapOr(""))
if err != nil {
return nil, err
}
nodeAnn := &lnwire.NodeAnnouncement1{
Features: n.Features.RawFeatureVector,
NodeID: n.PubKeyBytes,
RGBColor: n.Color.UnwrapOr(color.RGBA{}),
Alias: alias,
Addresses: n.Addresses,
Timestamp: uint32(n.LastUpdate.Unix()),
ExtraOpaqueData: n.ExtraOpaqueData,
}
if !signed {
return nodeAnn, nil
}
sig, err := lnwire.NewSigFromECDSARawSignature(n.AuthSigBytes)
if err != nil {
return nil, err
}
nodeAnn.Signature = sig
return nodeAnn, nil
}
// NodeFromWireAnnouncement creates a Node instance from an
// lnwire.NodeAnnouncement1 message.
func NodeFromWireAnnouncement(msg *lnwire.NodeAnnouncement1) *Node {
timestamp := time.Unix(int64(msg.Timestamp), 0)
return NewV1Node(
msg.NodeID,
&NodeV1Fields{
LastUpdate: timestamp,
Addresses: msg.Addresses,
Alias: msg.Alias.String(),
AuthSigBytes: msg.Signature.ToSignatureBytes(),
Features: msg.Features,
Color: msg.RGBColor,
ExtraOpaqueData: msg.ExtraOpaqueData,
},
)
}