multi: add models.Node V1 constructor

Add a version field to models.Node and a V1 constructor for it.
This commit is contained in:
Elle Mouton authored and yyforyongyu committed 2025-11-12 22:54:05 +08:00
1 parent 8f205e2d1c
commit 80e70096cb
18 files changed
+443 -340

No files matched your search

+35 -34
View File
@@ -417,20 +417,20 @@ func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey,
case errors.Is(err, graphdb.ErrGraphNodeNotFound):
fallthrough
case errors.Is(err, graphdb.ErrGraphNotFound):
graphNode := &models.Node{
Addresses: []net.Addr{&net.TCPAddr{
IP: bytes.Repeat(
[]byte("a"), 16,
),
}},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
copy(
graphNode.PubKeyBytes[:],
pub.SerializeCompressed(),
//nolint:ll
graphNode := models.NewV1Node(
route.NewVertex(pub),
&models.NodeV1Fields{
Addresses: []net.Addr{&net.TCPAddr{
IP: bytes.Repeat(
[]byte("a"), 16,
),
}},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
).RawFeatureVector,
AuthSigBytes: testSig.Serialize(),
},
)
err := d.db.AddNode(
context.Background(), graphNode,
@@ -449,18 +449,18 @@ func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey,
if err != nil {
return nil, err
}
dbNode := &models.Node{
Addresses: []net.Addr{
&net.TCPAddr{
dbNode := models.NewV1Node(
route.NewVertex(nodeKey), &models.NodeV1Fields{
Addresses: []net.Addr{&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
}},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
).RawFeatureVector,
AuthSigBytes: testSig.Serialize(),
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
copy(dbNode.PubKeyBytes[:], nodeKey.SerializeCompressed())
)
if err := d.db.AddNode(
context.Background(), dbNode,
); err != nil {
@@ -549,18 +549,19 @@ func (d *testDBGraph) addRandNode() (*btcec.PublicKey, error) {
if err != nil {
return nil, err
}
dbNode := &models.Node{
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
dbNode := models.NewV1Node(
route.NewVertex(nodeKey), &models.NodeV1Fields{
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
).RawFeatureVector,
AuthSigBytes: testSig.Serialize(),
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
copy(dbNode.PubKeyBytes[:], nodeKey.SerializeCompressed())
)
err = d.db.AddNode(context.Background(), dbNode)
if err != nil {
return nil, err
+12 -9
View File
@@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/shachain"
"github.com/stretchr/testify/require"
)
@@ -811,15 +812,17 @@ func createNode(priv *btcec.PrivateKey) *models.Node {
updateTime := rand.Int63()
pub := priv.PubKey().SerializeCompressed()
n := &models.Node{
AuthSigBytes: testSig.Serialize(),
LastUpdate: time.Unix(updateTime, 0),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + string(pub),
Features: testFeatures,
Addresses: testAddrs,
}
copy(n.PubKeyBytes[:], priv.PubKey().SerializeCompressed())
n := models.NewV1Node(
route.NewVertex(priv.PubKey()),
&models.NodeV1Fields{
AuthSigBytes: testSig.Serialize(),
LastUpdate: time.Unix(updateTime, 0),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + string(pub),
Features: testFeatures.RawFeatureVector,
Addresses: testAddrs,
},
)
return n
}
+35 -31
View File
@@ -97,15 +97,16 @@ func TestIgnoreNodeAnnouncement(t *testing.T) {
ctx := createTestCtxFromFile(t, startingBlockHeight, basicGraphFilePath)
pub := priv1.PubKey()
node := &models.Node{
LastUpdate: time.Unix(123, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node11",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures,
}
copy(node.PubKeyBytes[:], pub.SerializeCompressed())
node := models.NewV1Node(
route.NewVertex(pub), &models.NodeV1Fields{
Addresses: testAddrs,
AuthSigBytes: testSig.Serialize(),
Features: testFeatures.RawFeatureVector,
LastUpdate: time.Unix(123, 0),
Color: color.RGBA{1, 2, 3, 0},
Alias: "node11",
},
)
err := ctx.builder.AddNode(t.Context(), node)
if !IsError(err, ErrIgnored) {
@@ -1083,15 +1084,16 @@ func TestIsStaleNode(t *testing.T) {
// With the node stub in the database, we'll add the fully node
// announcement to the database.
n1 := &models.Node{
LastUpdate: updateTimeStamp,
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node11",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures,
}
copy(n1.PubKeyBytes[:], priv1.PubKey().SerializeCompressed())
n1 := models.NewV1Node(
route.NewVertex(priv1.PubKey()), &models.NodeV1Fields{
LastUpdate: updateTimeStamp,
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node11",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures.RawFeatureVector,
},
)
if err := ctx.builder.AddNode(t.Context(), n1); err != nil {
t.Fatalf("could not add node: %v", err)
}
@@ -1399,14 +1401,16 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
return nil, err
}
dbNode := &models.Node{
pubKey, err := route.NewVertexFromBytes(pubBytes)
require.NoError(t, err)
dbNode := models.NewV1Node(pubKey, &models.NodeV1Fields{
AuthSigBytes: testSig.Serialize(),
LastUpdate: testTime,
Addresses: testAddrs,
Alias: node.Alias,
Features: testFeatures,
}
copy(dbNode.PubKeyBytes[:], pubBytes)
Features: testFeatures.RawFeatureVector,
})
// We require all aliases within the graph to be unique for our
// tests.
@@ -1784,15 +1788,15 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
features = lnwire.EmptyFeatureVector()
}
dbNode := &models.Node{
AuthSigBytes: testSig.Serialize(),
LastUpdate: testTime,
Addresses: testAddrs,
Alias: alias,
Features: features,
}
copy(dbNode.PubKeyBytes[:], pubKey.SerializeCompressed())
dbNode := models.NewV1Node(
route.NewVertex(pubKey), &models.NodeV1Fields{
AuthSigBytes: testSig.Serialize(),
LastUpdate: testTime,
Addresses: testAddrs,
Alias: alias,
Features: features.RawFeatureVector,
},
)
privKeyMap[alias] = privKey
+31 -37
View File
@@ -70,18 +70,18 @@ var (
)
func createNode(priv *btcec.PrivateKey) *models.Node {
pub := priv.PubKey().SerializeCompressed()
n := &models.Node{
AuthSigBytes: testSig.Serialize(),
LastUpdate: nextUpdateTime(),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + hex.EncodeToString(pub),
Features: testFeatures,
Addresses: testAddrs,
}
copy(n.PubKeyBytes[:], priv.PubKey().SerializeCompressed())
pubKey := route.NewVertex(priv.PubKey())
return n
return models.NewV1Node(
pubKey, &models.NodeV1Fields{
LastUpdate: nextUpdateTime(),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + hex.EncodeToString(pubKey[:]),
Addresses: testAddrs,
Features: testFeatures.RawFeatureVector,
AuthSigBytes: testSig.Serialize(),
},
)
}
func createTestVertex(t testing.TB) *models.Node {
@@ -105,16 +105,18 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
timeStamp := int64(1232342)
nodeWithAddrs := func(addrs []net.Addr) *models.Node {
timeStamp++
return &models.Node{
AuthSigBytes: testSig.Serialize(),
LastUpdate: time.Unix(timeStamp, 0),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek",
Features: testFeatures,
Addresses: addrs,
ExtraOpaqueData: []byte{1, 1, 1, 2, 2, 2, 2},
PubKeyBytes: testPub,
}
return models.NewV1Node(
testPub, &models.NodeV1Fields{
AuthSigBytes: testSig.Serialize(),
LastUpdate: time.Unix(timeStamp, 0),
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek",
Features: testFeatures.RawFeatureVector,
Addresses: addrs,
ExtraOpaqueData: []byte{1, 1, 1, 2, 2, 2, 2},
},
)
}
// First, insert the node into the graph DB. This should succeed
@@ -313,11 +315,7 @@ func TestPartialNode(t *testing.T) {
// The two nodes should match exactly! (with default values for
// LastUpdate and db set to satisfy compareNodes())
expectedNode1 := &models.Node{
LastUpdate: time.Unix(0, 0),
PubKeyBytes: pubKey1,
Features: lnwire.EmptyFeatureVector(),
}
expectedNode1 := models.NewV1ShellNode(pubKey1)
compareNodes(t, expectedNode1, dbNode1)
_, exists, err = graph.HasNode(ctx, dbNode2.PubKeyBytes)
@@ -326,11 +324,7 @@ func TestPartialNode(t *testing.T) {
// The two nodes should match exactly! (with default values for
// LastUpdate and db set to satisfy compareNodes())
expectedNode2 := &models.Node{
LastUpdate: time.Unix(0, 0),
PubKeyBytes: pubKey2,
Features: lnwire.EmptyFeatureVector(),
}
expectedNode2 := models.NewV1ShellNode(pubKey2)
compareNodes(t, expectedNode2, dbNode2)
// Next, delete the node from the graph, this should purge all data
@@ -365,7 +359,7 @@ func TestAliasLookup(t *testing.T) {
require.NoError(t, err, "unable to generate pubkey")
dbAlias, err := graph.LookupAlias(ctx, nodePub)
require.NoError(t, err, "unable to find alias")
require.Equal(t, testNode.Alias, dbAlias)
require.Equal(t, testNode.Alias.UnwrapOr(""), dbAlias)
// Ensure that looking up a non-existent alias results in an error.
node := createTestVertex(t)
@@ -1600,7 +1594,7 @@ func fillTestGraph(t testing.TB, graph *ChannelGraph, numNodes,
node := createTestVertex(t)
nodes[i] = node
nodeIndex[node.Alias] = struct{}{}
nodeIndex[node.Alias.UnwrapOr("")] = struct{}{}
}
// Add each of the nodes into the graph, they should be inserted
@@ -1612,7 +1606,7 @@ func fillTestGraph(t testing.TB, graph *ChannelGraph, numNodes,
// Iterate over each node as returned by the graph, if all nodes are
// reached, then the map created above should be empty.
err := graph.ForEachNode(ctx, func(n *models.Node) error {
delete(nodeIndex, n.Alias)
delete(nodeIndex, n.Alias.UnwrapOr(""))
return nil
}, func() {})
require.NoError(t, err)
@@ -2289,7 +2283,7 @@ func TestNodeUpdatesInHorizon(t *testing.T) {
require.Len(t, resp, len(queryCase.resp))
for i := 0; i < len(resp); i++ {
compareNodes(t, &queryCase.resp[i], &resp[i])
compareNodes(t, &queryCase.resp[i], resp[i])
}
}
}
@@ -2487,7 +2481,7 @@ func TestNodeUpdatesInHorizonEarlyTermination(t *testing.T) {
)
// Collect only up to stopAt nodes, breaking afterwards.
var collected []models.Node
var collected []*models.Node
count := 0
for node := range iter {
if count >= stopAt {
@@ -3824,7 +3818,7 @@ func TestNodePruningUpdateIndexDeletion(t *testing.T) {
t.Fatalf("should have 1 nodes instead have: %v",
len(nodesInHorizon))
}
compareNodes(t, node1, &nodesInHorizon[0])
compareNodes(t, node1, nodesInHorizon[0])
// We'll now delete the node from the graph, this should result in it
// being removed from the update index as well.
+1 -1
View File
@@ -111,7 +111,7 @@ type V1Store interface { //nolint:interfacebloat
// by two nodes to quickly determine if they have the same set of up to
// date node announcements.
NodeUpdatesInHorizon(startTime, endTime time.Time,
opts ...IteratorOption) iter.Seq2[models.Node, error]
opts ...IteratorOption) iter.Seq2[*models.Node, error]
// FetchNode attempts to look up a target node by its identity
// public key. If the node isn't found in the database, then
+57 -57
View File
@@ -7,6 +7,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"image/color"
"io"
"iter"
"math"
@@ -848,7 +849,7 @@ func forEachNode(db kvdb.Backend,
// Execute the callback, the transaction will abort if
// this returns an error.
return cb(tx, &node)
return cb(tx, node)
})
}
@@ -945,12 +946,7 @@ func sourceNodeWithTx(nodes kvdb.RBucket) (*models.Node, error) {
// With the pubKey of the source node retrieved, we're able to
// fetch the full node information.
node, err := fetchLightningNode(nodes, selfPub)
if err != nil {
return nil, err
}
return &node, nil
return fetchLightningNode(nodes, selfPub)
}
// SetSourceNode sets the source node within the graph database. The source
@@ -1205,10 +1201,9 @@ func (c *KVStore) addChannelEdge(tx kvdb.RwTx,
_, node1Err := fetchLightningNode(nodes, edge.NodeKey1Bytes[:])
switch {
case errors.Is(node1Err, ErrGraphNodeNotFound):
node1Shell := models.Node{
PubKeyBytes: edge.NodeKey1Bytes,
}
err := addLightningNode(tx, &node1Shell)
err := addLightningNode(
tx, models.NewV1ShellNode(edge.NodeKey1Bytes),
)
if err != nil {
return fmt.Errorf("unable to create shell node "+
"for: %x: %w", edge.NodeKey1Bytes, err)
@@ -1220,10 +1215,9 @@ func (c *KVStore) addChannelEdge(tx kvdb.RwTx,
_, node2Err := fetchLightningNode(nodes, edge.NodeKey2Bytes[:])
switch {
case errors.Is(node2Err, ErrGraphNodeNotFound):
node2Shell := models.Node{
PubKeyBytes: edge.NodeKey2Bytes,
}
err := addLightningNode(tx, &node2Shell)
err := addLightningNode(
tx, models.NewV1ShellNode(edge.NodeKey2Bytes),
)
if err != nil {
return fmt.Errorf("unable to create shell node "+
"for: %x: %w", edge.NodeKey2Bytes, err)
@@ -2247,8 +2241,8 @@ func (c *KVStore) fetchNextChanUpdateBatch(
Info: &edgeInfo,
Policy1: edge1,
Policy2: edge2,
Node1: &node1,
Node2: &node2,
Node1: node1,
Node2: node2,
}
state.edgesSeen[chanIDInt] = struct{}{}
@@ -2394,10 +2388,10 @@ func newNodeUpdatesIterator(batchSize int, startTime, endTime time.Time,
// fetchNextNodeBatch fetches the next batch of node announcements using the
// iterator state.
func (c *KVStore) fetchNextNodeBatch(
state *nodeUpdatesIterator) ([]models.Node, bool, error) {
state *nodeUpdatesIterator) ([]*models.Node, bool, error) {
var (
nodeBatch []models.Node
nodeBatch []*models.Node
hasMore bool
)
@@ -2536,14 +2530,14 @@ func (c *KVStore) fetchNextNodeBatch(
// update timestamp within the passed range.
func (c *KVStore) NodeUpdatesInHorizon(startTime,
endTime time.Time,
opts ...IteratorOption) iter.Seq2[models.Node, error] {
opts ...IteratorOption) iter.Seq2[*models.Node, error] {
cfg := defaultIteratorConfig()
for _, opt := range opts {
opt(cfg)
}
return func(yield func(models.Node, error) bool) {
return func(yield func(*models.Node, error) bool) {
// Initialize iterator state.
state := newNodeUpdatesIterator(
cfg.nodeUpdateIterBatchSize,
@@ -2557,7 +2551,7 @@ func (c *KVStore) NodeUpdatesInHorizon(startTime,
log.Errorf("unable to read node updates in "+
"horizon: %v", err)
yield(models.Node{}, err)
yield(&models.Node{}, err)
return
}
@@ -2945,8 +2939,8 @@ func (c *KVStore) fetchChanInfos(tx kvdb.RTx, chanIDs []uint64) (
Info: &edgeInfo,
Policy1: edge1,
Policy2: edge2,
Node1: &node1,
Node2: &node2,
Node1: node1,
Node2: node2,
})
}
@@ -3411,7 +3405,7 @@ func (c *KVStore) fetchLightningNode(tx kvdb.RTx,
return err
}
node = &n
node = n
return nil
}
@@ -3694,7 +3688,7 @@ func (c *KVStore) fetchOtherNode(tx kvdb.RTx,
return err
}
targetNode = &node
targetNode = node
return nil
}
@@ -4383,17 +4377,20 @@ func putLightningNode(nodeBucket, aliasBucket, updateIndex kvdb.RwBucket,
return err
}
if err := binary.Write(&b, byteOrder, node.Color.R); err != nil {
nodeColor := node.Color.UnwrapOr(color.RGBA{})
if err := binary.Write(&b, byteOrder, nodeColor.R); err != nil {
return err
}
if err := binary.Write(&b, byteOrder, node.Color.G); err != nil {
if err := binary.Write(&b, byteOrder, nodeColor.G); err != nil {
return err
}
if err := binary.Write(&b, byteOrder, node.Color.B); err != nil {
if err := binary.Write(&b, byteOrder, nodeColor.B); err != nil {
return err
}
if err := wire.WriteVarString(&b, 0, node.Alias); err != nil {
err = wire.WriteVarString(&b, 0, node.Alias.UnwrapOr(""))
if err != nil {
return err
}
@@ -4432,7 +4429,8 @@ func putLightningNode(nodeBucket, aliasBucket, updateIndex kvdb.RwBucket,
return err
}
if err := aliasBucket.Put(nodePub, []byte(node.Alias)); err != nil {
err = aliasBucket.Put(nodePub, []byte(node.Alias.UnwrapOr("")))
if err != nil {
return err
}
@@ -4466,11 +4464,11 @@ func putLightningNode(nodeBucket, aliasBucket, updateIndex kvdb.RwBucket,
}
func fetchLightningNode(nodeBucket kvdb.RBucket,
nodePub []byte) (models.Node, error) {
nodePub []byte) (*models.Node, error) {
nodeBytes := nodeBucket.Get(nodePub)
if nodeBytes == nil {
return models.Node{}, ErrGraphNodeNotFound
return nil, ErrGraphNodeNotFound
}
nodeReader := bytes.NewReader(nodeBytes)
@@ -4533,30 +4531,29 @@ func deserializeLightningNodeCacheable(r io.Reader) (route.Vertex,
return pubKey, features, nil
}
func deserializeLightningNode(r io.Reader) (models.Node, error) {
func deserializeLightningNode(r io.Reader) (*models.Node, error) {
var (
node models.Node
scratch [8]byte
err error
pubKey [33]byte
)
// Always populate a feature vector, even if we don't have a node
// announcement and short circuit below.
node.Features = lnwire.EmptyFeatureVector()
if _, err := r.Read(scratch[:]); err != nil {
return models.Node{}, err
return nil, err
}
unix := int64(byteOrder.Uint64(scratch[:]))
node.LastUpdate = time.Unix(unix, 0)
lastUpdate := time.Unix(unix, 0)
if _, err := io.ReadFull(r, node.PubKeyBytes[:]); err != nil {
return models.Node{}, err
if _, err := io.ReadFull(r, pubKey[:]); err != nil {
return nil, err
}
node := models.NewV1ShellNode(pubKey)
node.LastUpdate = lastUpdate
if _, err := r.Read(scratch[:2]); err != nil {
return models.Node{}, err
return nil, err
}
hasNodeAnn := byteOrder.Uint16(scratch[:2])
@@ -4568,28 +4565,31 @@ func deserializeLightningNode(r io.Reader) (models.Node, error) {
// We did get a node announcement for this node, so we'll have the rest
// of the data available.
if err := binary.Read(r, byteOrder, &node.Color.R); err != nil {
return models.Node{}, err
var nodeColor color.RGBA
if err := binary.Read(r, byteOrder, &nodeColor.R); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &node.Color.G); err != nil {
return models.Node{}, err
if err := binary.Read(r, byteOrder, &nodeColor.G); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &node.Color.B); err != nil {
return models.Node{}, err
if err := binary.Read(r, byteOrder, &nodeColor.B); err != nil {
return nil, err
}
node.Color = fn.Some(nodeColor)
node.Alias, err = wire.ReadVarString(r, 0)
alias, err := wire.ReadVarString(r, 0)
if err != nil {
return models.Node{}, err
return nil, err
}
node.Alias = fn.Some(alias)
err = node.Features.Decode(r)
if err != nil {
return models.Node{}, err
return nil, err
}
if _, err := r.Read(scratch[:2]); err != nil {
return models.Node{}, err
return nil, err
}
numAddresses := int(byteOrder.Uint16(scratch[:2]))
@@ -4597,7 +4597,7 @@ func deserializeLightningNode(r io.Reader) (models.Node, error) {
for i := 0; i < numAddresses; i++ {
address, err := DeserializeAddr(r)
if err != nil {
return models.Node{}, err
return nil, err
}
addresses = append(addresses, address)
}
@@ -4605,7 +4605,7 @@ func deserializeLightningNode(r io.Reader) (models.Node, error) {
node.AuthSigBytes, err = wire.ReadVarBytes(r, 0, 80, "sig")
if err != nil {
return models.Node{}, err
return nil, err
}
// We'll try and see if there are any opaque bytes left, if not, then
@@ -4617,7 +4617,7 @@ func deserializeLightningNode(r io.Reader) (models.Node, error) {
case errors.Is(err, io.ErrUnexpectedEOF):
case errors.Is(err, io.EOF):
case err != nil:
return models.Node{}, err
return nil, err
}
if len(extraBytes) > 0 {
+87 -15
View File
@@ -7,7 +7,9 @@ import (
"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.
@@ -15,6 +17,9 @@ import (
// 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
pubKey *btcec.PublicKey
@@ -27,11 +32,11 @@ type Node struct {
Addresses []net.Addr
// Color is the selected color for the node.
Color color.RGBA
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 string
Alias fn.Option[string]
// AuthSigBytes is the raw signature under the advertised public key
// which serves to authenticate the attributes announced by this node.
@@ -49,6 +54,72 @@ type Node struct {
ExtraOpaqueData []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,
}
}
// 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.
@@ -85,7 +156,7 @@ func (n *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
return nil, fmt.Errorf("node does not have node announcement")
}
alias, err := lnwire.NewNodeAlias(n.Alias)
alias, err := lnwire.NewNodeAlias(n.Alias.UnwrapOr(""))
if err != nil {
return nil, err
}
@@ -93,7 +164,7 @@ func (n *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
nodeAnn := &lnwire.NodeAnnouncement1{
Features: n.Features.RawFeatureVector,
NodeID: n.PubKeyBytes,
RGBColor: n.Color,
RGBColor: n.Color.UnwrapOr(color.RGBA{}),
Alias: alias,
Addresses: n.Addresses,
Timestamp: uint32(n.LastUpdate.Unix()),
@@ -118,16 +189,17 @@ func (n *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
// lnwire.NodeAnnouncement1 message.
func NodeFromWireAnnouncement(msg *lnwire.NodeAnnouncement1) *Node {
timestamp := time.Unix(int64(msg.Timestamp), 0)
features := lnwire.NewFeatureVector(msg.Features, lnwire.Features)
return &Node{
LastUpdate: timestamp,
Addresses: msg.Addresses,
PubKeyBytes: msg.NodeID,
Alias: msg.Alias.String(),
AuthSigBytes: msg.Signature.ToSignatureBytes(),
Features: features,
Color: msg.RGBColor,
ExtraOpaqueData: msg.ExtraOpaqueData,
}
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,
},
)
}
+5 -3
View File
@@ -391,9 +391,11 @@ func (c *ChannelGraph) addToTopologyChange(update *TopologyChange,
nodeUpdate := &NetworkNodeUpdate{
Addresses: m.Addresses,
IdentityKey: pubKey,
Alias: m.Alias,
Color: EncodeHexColor(m.Color),
Features: m.Features.Clone(),
Alias: m.Alias.UnwrapOr(""),
Color: EncodeHexColor(
m.Color.UnwrapOr(color.RGBA{}),
),
Features: m.Features.Clone(),
}
update.NodeUpdates = append(update.NodeUpdates, nodeUpdate)
+5 -2
View File
@@ -7,6 +7,7 @@ import (
"database/sql"
"errors"
"fmt"
"image/color"
"net"
"slices"
"time"
@@ -1451,8 +1452,10 @@ func insertNodeSQLMig(ctx context.Context, db SQLQueries,
if node.HaveAnnouncement() {
params.LastUpdate = sqldb.SQLInt64(node.LastUpdate.Unix())
params.Color = sqldb.SQLStrValid(EncodeHexColor(node.Color))
params.Alias = sqldb.SQLStrValid(node.Alias)
params.Color = sqldb.SQLStrValid(
EncodeHexColor(node.Color.UnwrapOr(color.RGBA{})),
)
params.Alias = sqldb.SQLStrValid(node.Alias.UnwrapOr(""))
params.Signature = node.AuthSigBytes
}
+12 -22
View File
@@ -384,9 +384,7 @@ func TestMigrateGraphToSQL(t *testing.T) {
// The PruneGraph call requires that the source
// node be set. So that is the first object
// we will write.
&models.Node{
PubKeyBytes: testPub,
},
models.NewV1ShellNode(testPub),
// Now we add some block heights to prune
// the graph at.
uint32(1), uint32(2), uint32(20), uint32(3),
@@ -746,16 +744,15 @@ type testNodeOpt func(*models.Node)
// makeTestNode can be used to create a test models.Node. The
// functional options can be used to modify the node's attributes.
func makeTestNode(t *testing.T, opts ...testNodeOpt) *models.Node {
n := &models.Node{
n := models.NewV1Node(genPubKey(t), &models.NodeV1Fields{
AuthSigBytes: testSigBytes,
LastUpdate: testTime,
Color: testColor,
Alias: "kek",
Features: testFeatures,
Features: testFeatures.RawFeatureVector,
Addresses: testAddrs,
ExtraOpaqueData: testExtraData,
PubKeyBytes: genPubKey(t),
}
})
for _, opt := range opts {
opt(n)
@@ -774,11 +771,7 @@ func makeTestNode(t *testing.T, opts ...testNodeOpt) *models.Node {
func makeTestShellNode(t *testing.T,
opts ...testNodeOpt) *models.Node {
n := &models.Node{
PubKeyBytes: genPubKey(t),
Features: testEmptyFeatures,
LastUpdate: time.Unix(0, 0),
}
n := models.NewV1ShellNode(genPubKey(t))
for _, opt := range opts {
opt(n)
@@ -1813,18 +1806,15 @@ func genRandomNode(t *rapid.T) *models.Node {
extraOpaqueData = nil
}
node := &models.Node{
AuthSigBytes: sigBytes,
LastUpdate: randTime,
Color: randColor,
Alias: alias.String(),
Features: lnwire.NewFeatureVector(
features, lnwire.Features,
),
node := models.NewV1Node(pubKeyBytes, &models.NodeV1Fields{
AuthSigBytes: sigBytes,
LastUpdate: randTime,
Color: randColor,
Alias: alias.String(),
Features: features,
Addresses: addrs,
ExtraOpaqueData: extraOpaqueData,
PubKeyBytes: pubKeyBytes,
}
})
// We call this method so that the internal pubkey field is populated
// which then lets us to proper struct comparison later on.
+38 -17
View File
@@ -7,6 +7,7 @@ import (
"encoding/hex"
"errors"
"fmt"
color "image/color"
"iter"
"maps"
"math"
@@ -555,14 +556,14 @@ func (s *SQLStore) SetSourceNode(ctx context.Context,
//
// NOTE: This is part of the V1Store interface.
func (s *SQLStore) NodeUpdatesInHorizon(startTime, endTime time.Time,
opts ...IteratorOption) iter.Seq2[models.Node, error] {
opts ...IteratorOption) iter.Seq2[*models.Node, error] {
cfg := defaultIteratorConfig()
for _, opt := range opts {
opt(cfg)
}
return func(yield func(models.Node, error) bool) {
return func(yield func(*models.Node, error) bool) {
var (
ctx = context.TODO()
lastUpdateTime sql.NullInt64
@@ -573,7 +574,7 @@ func (s *SQLStore) NodeUpdatesInHorizon(startTime, endTime time.Time,
// Each iteration, we'll read a batch amount of nodes, yield
// them, then decide is we have more or not.
for hasMore {
var batch []models.Node
var batch []*models.Node
//nolint:ll
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
@@ -607,7 +608,7 @@ func (s *SQLStore) NodeUpdatesInHorizon(startTime, endTime time.Time,
err = forEachNodeInBatch(
ctx, s.cfg.QueryCfg, db, rows,
func(_ int64, node *models.Node) error {
batch = append(batch, *node)
batch = append(batch, node)
// Update pagination cursors
// based on the last processed
@@ -629,14 +630,14 @@ func (s *SQLStore) NodeUpdatesInHorizon(startTime, endTime time.Time,
return nil
}, func() {
batch = []models.Node{}
batch = []*models.Node{}
})
if err != nil {
log.Errorf("NodeUpdatesInHorizon batch "+
"error: %v", err)
yield(models.Node{}, err)
yield(&models.Node{}, err)
return
}
@@ -3485,27 +3486,30 @@ func buildNodeWithBatchData(dbNode sqlc.GraphNode,
var pub [33]byte
copy(pub[:], dbNode.PubKey)
node := &models.Node{
PubKeyBytes: pub,
Features: lnwire.EmptyFeatureVector(),
LastUpdate: time.Unix(0, 0),
}
node := models.NewV1ShellNode(pub)
if len(dbNode.Signature) == 0 {
return node, nil
}
node.AuthSigBytes = dbNode.Signature
node.Alias = dbNode.Alias.String
node.LastUpdate = time.Unix(dbNode.LastUpdate.Int64, 0)
if dbNode.Alias.Valid {
node.Alias = fn.Some(dbNode.Alias.String)
}
if dbNode.LastUpdate.Valid {
node.LastUpdate = time.Unix(dbNode.LastUpdate.Int64, 0)
}
var err error
if dbNode.Color.Valid {
node.Color, err = DecodeHexColor(dbNode.Color.String)
nodeColor, err := DecodeHexColor(dbNode.Color.String)
if err != nil {
return nil, fmt.Errorf("unable to decode color: %w",
err)
}
node.Color = fn.Some(nodeColor)
}
// Use preloaded features.
@@ -3608,9 +3612,26 @@ func upsertNode(ctx context.Context, db SQLQueries,
}
if node.HaveAnnouncement() {
params.LastUpdate = sqldb.SQLInt64(node.LastUpdate.Unix())
params.Color = sqldb.SQLStrValid(EncodeHexColor(node.Color))
params.Alias = sqldb.SQLStrValid(node.Alias)
switch node.Version {
case lnwire.GossipVersion1:
params.LastUpdate = sqldb.SQLInt64(
node.LastUpdate.Unix(),
)
case lnwire.GossipVersion2:
default:
return 0, fmt.Errorf("unknown gossip version: %d",
node.Version)
}
node.Color.WhenSome(func(rgba color.RGBA) {
params.Color = sqldb.SQLStrValid(EncodeHexColor(rgba))
})
node.Alias.WhenSome(func(s string) {
params.Alias = sqldb.SQLStrValid(s)
})
params.Signature = node.AuthSigBytes
}
+16 -18
View File
@@ -83,15 +83,16 @@ func createTestNode(t *testing.T) *models.Node {
require.NoError(t, err)
pub := priv.PubKey().SerializeCompressed()
n := &models.Node{
LastUpdate: time.Unix(updateTime, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + hex.EncodeToString(pub),
AuthSigBytes: testSig.Serialize(),
Features: testFeatures,
}
copy(n.PubKeyBytes[:], pub)
n := models.NewV1Node(
route.NewVertex(priv.PubKey()), &models.NodeV1Fields{
LastUpdate: time.Unix(updateTime, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + hex.EncodeToString(pub),
AuthSigBytes: testSig.Serialize(),
Features: testFeatures.RawFeatureVector,
},
)
return n
}
@@ -699,15 +700,12 @@ func TestNodeUpdateNotification(t *testing.T) {
t, testFeaturesBuf.Bytes(), featuresBuf.Bytes(),
)
if nodeUpdate.Alias != ann.Alias {
t.Fatalf("node alias doesn't match: expected %v, got %v",
ann.Alias, nodeUpdate.Alias)
}
if nodeUpdate.Color != graphdb.EncodeHexColor(ann.Color) {
t.Fatalf("node color doesn't match: expected %v, "+
"got %v", graphdb.EncodeHexColor(ann.Color),
nodeUpdate.Color)
}
require.Equal(t, nodeUpdate.Alias, ann.Alias.UnwrapOr(""))
require.Equal(
t, nodeUpdate.Color, graphdb.EncodeHexColor(
ann.Color.UnwrapOr(color.RGBA{}),
),
)
}
// Create lookup map for notifications we are intending to receive. Entries
+15 -16
View File
@@ -226,18 +226,7 @@ func (s *Server) ImportGraph(ctx context.Context,
var err error
for _, rpcNode := range graph.Nodes {
node := &models.Node{
LastUpdate: time.Unix(
int64(rpcNode.LastUpdate), 0,
),
Alias: rpcNode.Alias,
// NOTE: this is a workaround to ensure that
// HaveAnnouncement() returns true so that the other
// fields are properly persisted. However,
AuthSigBytes: []byte{0},
}
node.PubKeyBytes, err = parsePubKey(rpcNode.PubKey)
pubKeyBytes, err := parsePubKey(rpcNode.PubKey)
if err != nil {
return nil, err
}
@@ -254,15 +243,25 @@ func (s *Server) ImportGraph(ctx context.Context,
}
featureVector := lnwire.NewRawFeatureVector(featureBits...)
node.Features = lnwire.NewFeatureVector(
featureVector, featureNames,
)
node.Color, err = lncfg.ParseHexColor(rpcNode.Color)
nodeColor, err := lncfg.ParseHexColor(rpcNode.Color)
if err != nil {
return nil, err
}
node := models.NewV1Node(pubKeyBytes, &models.NodeV1Fields{
LastUpdate: time.Unix(
int64(rpcNode.LastUpdate), 0,
),
Alias: rpcNode.Alias,
Features: featureVector,
Color: nodeColor,
// NOTE: this is a workaround to ensure that
// HaveAnnouncement() returns true so that the other
// fields are properly persisted. However,
AuthSigBytes: []byte{0},
})
if err := graphDB.AddNode(ctx, node); err != nil {
return nil, fmt.Errorf("unable to add node %v: %w",
rpcNode.PubKey, err)
+21 -17
View File
@@ -228,14 +228,16 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
return nil, err
}
dbNode := &models.Node{
pubKey, err := route.NewVertexFromBytes(pubBytes)
require.NoError(t, err)
dbNode := models.NewV1Node(pubKey, &models.NodeV1Fields{
AuthSigBytes: testSig.Serialize(),
LastUpdate: testTime,
Addresses: testAddrs,
Alias: node.Alias,
Features: testFeatures,
}
copy(dbNode.PubKeyBytes[:], pubBytes)
Features: testFeatures.RawFeatureVector,
})
// We require all aliases within the graph to be unique for our
// tests.
@@ -564,15 +566,15 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
features = lnwire.EmptyFeatureVector()
}
dbNode := &models.Node{
AuthSigBytes: testSig.Serialize(),
LastUpdate: testTime,
Addresses: testAddrs,
Alias: alias,
Features: features,
}
copy(dbNode.PubKeyBytes[:], pubKey.SerializeCompressed())
dbNode := models.NewV1Node(
route.NewVertex(pubKey), &models.NodeV1Fields{
AuthSigBytes: testSig.Serialize(),
LastUpdate: testTime,
Addresses: testAddrs,
Alias: alias,
Features: features.RawFeatureVector,
},
)
privKeyMap[alias] = privKey
@@ -1249,10 +1251,12 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
dogePubKeyBytes, err := hex.DecodeString(dogePubKeyHex)
require.NoError(t, err, "unable to decode public key")
doge := &models.Node{}
copy(doge.PubKeyBytes[:], dogePubKeyBytes[:])
doge.Alias = "doge"
copy(doge.PubKeyBytes[:], dogePubKeyBytes)
pubKey, err := route.NewVertexFromBytes(dogePubKeyBytes)
require.NoError(t, err)
doge := models.NewV1Node(pubKey, &models.NodeV1Fields{
Alias: "doge",
})
graph.aliasMap["doge"] = doge.PubKeyBytes
// Create the channel edge going from songoku to doge and include it in
+3 -2
View File
@@ -89,8 +89,9 @@ func TestUpdateAdditionalEdge(t *testing.T) {
// Create a minimal test node using the private key priv1.
pub := priv1.PubKey().SerializeCompressed()
testNode := &models.Node{}
copy(testNode.PubKeyBytes[:], pub)
var pubKey [33]byte
copy(pubKey[:], pub)
testNode := models.NewV1ShellNode(pubKey)
nodeID, err := testNode.PubKey()
require.NoError(t, err, "failed to get node id")
+30 -27
View File
@@ -192,15 +192,16 @@ func createTestNode() (*models.Node, error) {
}
pub := priv.PubKey().SerializeCompressed()
n := &models.Node{
LastUpdate: time.Unix(updateTime, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + string(pub),
AuthSigBytes: testSig.Serialize(),
Features: testFeatures,
}
copy(n.PubKeyBytes[:], pub)
n := models.NewV1Node(
route.NewVertex(priv.PubKey()), &models.NodeV1Fields{
LastUpdate: time.Unix(updateTime, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "kek" + string(pub),
AuthSigBytes: testSig.Serialize(),
Features: testFeatures.RawFeatureVector,
},
)
return n, nil
}
@@ -2870,27 +2871,29 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// Now check that we can update the node info for the partial node
// without messing up the channel graph.
n1 := &models.Node{
LastUpdate: time.Unix(123, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node11",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures,
}
copy(n1.PubKeyBytes[:], priv1.PubKey().SerializeCompressed())
n1 := models.NewV1Node(
route.NewVertex(priv1.PubKey()), &models.NodeV1Fields{
LastUpdate: time.Unix(123, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node11",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures.RawFeatureVector,
},
)
require.NoError(t, ctx.graph.AddNode(ctxb, n1))
n2 := &models.Node{
LastUpdate: time.Unix(123, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node22",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures,
}
copy(n2.PubKeyBytes[:], priv2.PubKey().SerializeCompressed())
n2 := models.NewV1Node(
route.NewVertex(priv2.PubKey()), &models.NodeV1Fields{
LastUpdate: time.Unix(123, 0),
Addresses: testAddrs,
Color: color.RGBA{1, 2, 3, 0},
Alias: "node22",
AuthSigBytes: testSig.Serialize(),
Features: testFeatures.RawFeatureVector,
},
)
require.NoError(t, ctx.graph.AddNode(ctxb, n2))
+10 -7
View File
@@ -6,6 +6,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"image/color"
"io"
"maps"
"math"
@@ -7187,11 +7188,13 @@ func marshalNode(node *models.Node) *lnrpc.LightningNode {
customRecords := marshalExtraOpaqueData(node.ExtraOpaqueData)
return &lnrpc.LightningNode{
LastUpdate: uint32(node.LastUpdate.Unix()),
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
Addresses: nodeAddrs,
Alias: node.Alias,
Color: graphdb.EncodeHexColor(node.Color),
LastUpdate: uint32(node.LastUpdate.Unix()),
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
Addresses: nodeAddrs,
Alias: node.Alias.UnwrapOr(""),
Color: graphdb.EncodeHexColor(
node.Color.UnwrapOr(color.RGBA{}),
),
Features: features,
CustomRecords: customRecords,
}
@@ -8258,9 +8261,9 @@ func (r *rpcServer) ForwardingHistory(ctx context.Context,
}
// Cache the peer alias.
chanToPeerAlias[chanID] = peer.Alias
chanToPeerAlias[chanID] = peer.Alias.UnwrapOr("")
return peer.Alias, nil
return peer.Alias.UnwrapOr(""), nil
}
// TODO(roasbeef): add settlement latency?
+30 -25
View File
@@ -7,6 +7,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"image/color"
"math/big"
prand "math/rand"
"net"
@@ -3299,17 +3300,17 @@ func (s *server) createNewHiddenService(ctx context.Context) error {
// Finally, we'll update the on-disk version of our announcement so it
// will eventually propagate to nodes in the network.
selfNode := &models.Node{
LastUpdate: time.Unix(int64(newNodeAnn.Timestamp), 0),
Addresses: newNodeAnn.Addresses,
Alias: newNodeAnn.Alias.String(),
Features: lnwire.NewFeatureVector(
newNodeAnn.Features, lnwire.Features,
),
Color: newNodeAnn.RGBColor,
AuthSigBytes: newNodeAnn.Signature.ToSignatureBytes(),
}
copy(selfNode.PubKeyBytes[:], s.identityECDH.PubKey().SerializeCompressed())
selfNode := models.NewV1Node(
route.NewVertex(s.identityECDH.PubKey()), &models.NodeV1Fields{
Addresses: newNodeAnn.Addresses,
Features: newNodeAnn.Features,
AuthSigBytes: newNodeAnn.Signature.ToSignatureBytes(),
Color: newNodeAnn.RGBColor,
Alias: newNodeAnn.Alias.String(),
LastUpdate: time.Unix(int64(newNodeAnn.Timestamp), 0),
},
)
if err := s.graphDB.SetSourceNode(ctx, selfNode); err != nil {
return fmt.Errorf("can't set self node: %w", err)
}
@@ -3425,9 +3426,9 @@ func (s *server) updateAndBroadcastSelfNode(ctx context.Context,
selfNode.LastUpdate = time.Unix(int64(newNodeAnn.Timestamp), 0)
selfNode.Addresses = newNodeAnn.Addresses
selfNode.Alias = newNodeAnn.Alias.String()
selfNode.Alias = fn.Some(newNodeAnn.Alias.String())
selfNode.Features = s.featureMgr.Get(feature.SetNodeAnn)
selfNode.Color = newNodeAnn.RGBColor
selfNode.Color = fn.Some(newNodeAnn.RGBColor)
selfNode.AuthSigBytes = newNodeAnn.Signature.ToSignatureBytes()
copy(selfNode.PubKeyBytes[:], s.identityECDH.PubKey().SerializeCompressed())
@@ -5583,7 +5584,7 @@ func (s *server) setSelfNode(ctx context.Context, nodePub route.Vertex,
// Parse the color from config. We will update this later if the config
// color is not changed from default (#3399FF) and we have a value in
// the source node.
color, err := lncfg.ParseHexColor(s.cfg.Color)
nodeColor, err := lncfg.ParseHexColor(s.cfg.Color)
if err != nil {
return fmt.Errorf("unable to parse color: %w", err)
}
@@ -5607,13 +5608,17 @@ func (s *server) setSelfNode(ctx context.Context, nodePub route.Vertex,
// didn't specify a different color in the config. We'll use the
// source node's color.
if s.cfg.Color == defaultColor {
color = srcNode.Color
srcNode.Color.WhenSome(func(rgba color.RGBA) {
nodeColor = rgba
})
}
// If an alias is not specified in the config, we'll use the
// source node's alias.
if alias == "" {
alias = srcNode.Alias
srcNode.Alias.WhenSome(func(s string) {
alias = s
})
}
// If the `externalip` is not specified in the config, it means
@@ -5643,15 +5648,15 @@ func (s *server) setSelfNode(ctx context.Context, nodePub route.Vertex,
// TODO(abdulkbk): potentially find a way to use the source node's
// features in the self node.
selfNode := &models.Node{
LastUpdate: nodeLastUpdate,
Addresses: addrs,
Alias: nodeAlias.String(),
Color: color,
Features: s.featureMgr.Get(feature.SetNodeAnn),
}
copy(selfNode.PubKeyBytes[:], nodePub[:])
selfNode := models.NewV1Node(
nodePub, &models.NodeV1Fields{
Alias: nodeAlias.String(),
Color: nodeColor,
LastUpdate: nodeLastUpdate,
Addresses: addrs,
Features: s.featureMgr.GetRaw(feature.SetNodeAnn),
},
)
// Based on the disk representation of the node announcement generated
// above, we'll generate a node announcement that can go out on the