Skip to content
Merged
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
16 changes: 1 addition & 15 deletions discovery/gossiper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1982,21 +1982,7 @@ func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement,
err)
}

timestamp := time.Unix(int64(msg.Timestamp), 0)
features := lnwire.NewFeatureVector(msg.Features, lnwire.Features)
node := &models.LightningNode{
HaveNodeAnnouncement: true,
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 d.cfg.Graph.AddNode(node, op...)
return d.cfg.Graph.AddNode(models.NodeFromWireAnnouncement(msg), op...)
}

// isPremature decides whether a given network message has a block height+delta
Expand Down
4 changes: 4 additions & 0 deletions docs/release-notes/release-notes-0.19.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
* [Fixed a bug](https://github.com/lightningnetwork/lnd/pull/9322) that caused
estimateroutefee to ignore the default payment timeout.

* [Fix a bug](https://github.com/lightningnetwork/lnd/pull/9474) where LND would
fail to persist (and hence, propagate) node announcements containing address
types (such as a DNS hostname) unknown to LND.

# New Features

* [Support](https://github.com/lightningnetwork/lnd/pull/8390) for
Expand Down
49 changes: 49 additions & 0 deletions graph/db/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net"

"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tor"
)

Expand All @@ -26,6 +27,10 @@ const (

// v3OnionAddr denotes a version 3 Tor (prop224) onion service address.
v3OnionAddr addressType = 3

// opaqueAddrs denotes an address (or a set of addresses) that LND was
// not able to parse since LND is not yet aware of the address type.
opaqueAddrs addressType = 4
)

// encodeTCPAddr serializes a TCP address into its compact raw bytes
Expand Down Expand Up @@ -121,6 +126,27 @@ func encodeOnionAddr(w io.Writer, addr *tor.OnionAddr) error {
return nil
}

// encodeOpaqueAddrs serializes the lnwire.OpaqueAddrs type to a raw set of
// bytes that we will persist.
func encodeOpaqueAddrs(w io.Writer, addr *lnwire.OpaqueAddrs) error {
Comment thread
ellemouton marked this conversation as resolved.
// Write the type byte.
if _, err := w.Write([]byte{byte(opaqueAddrs)}); err != nil {
return err
}

// Write the length of the payload.
var l [2]byte
binary.BigEndian.PutUint16(l[:], uint16(len(addr.Payload)))
if _, err := w.Write(l[:]); err != nil {
return err
}

// Write the payload.
_, err := w.Write(addr.Payload)

return err
}

// DeserializeAddr reads the serialized raw representation of an address and
// deserializes it into the actual address. This allows us to avoid address
// resolution within the channeldb package.
Expand All @@ -147,6 +173,7 @@ func DeserializeAddr(r io.Reader) (net.Addr, error) {
IP: net.IP(ip[:]),
Port: int(binary.BigEndian.Uint16(port[:])),
}

case tcp6Addr:
var ip [16]byte
if _, err := r.Read(ip[:]); err != nil {
Expand All @@ -162,6 +189,7 @@ func DeserializeAddr(r io.Reader) (net.Addr, error) {
IP: net.IP(ip[:]),
Port: int(binary.BigEndian.Uint16(port[:])),
}

case v2OnionAddr:
var h [tor.V2DecodedLen]byte
if _, err := r.Read(h[:]); err != nil {
Expand All @@ -181,6 +209,7 @@ func DeserializeAddr(r io.Reader) (net.Addr, error) {
OnionService: onionService,
Port: port,
}

case v3OnionAddr:
var h [tor.V3DecodedLen]byte
if _, err := r.Read(h[:]); err != nil {
Expand All @@ -200,6 +229,24 @@ func DeserializeAddr(r io.Reader) (net.Addr, error) {
OnionService: onionService,
Port: port,
}

case opaqueAddrs:
Comment thread
ziggie1984 marked this conversation as resolved.
// Read the length of the payload.
var l [2]byte
if _, err := r.Read(l[:]); err != nil {
return nil, err
}

// Read the payload.
payload := make([]byte, binary.BigEndian.Uint16(l[:]))
if _, err := r.Read(payload); err != nil {
return nil, err
}

address = &lnwire.OpaqueAddrs{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Feels weird to have this in the lnwire package, while lnwire stores all the messages defined in BOLTs, not sub fields, guess it's pre-existing. Also didn't know we support more addr types in addition to the five defined in bolt7.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

it's a bit of a strange one but i actually think it might make sense to keep it in lnwire given that currently, this is what the node_announcement address field is:

[u16:addrlen]
[addrlen*byte:addresses]

and so then this addresses field includes any of those 5 types along with any extras that we dont know yet. So to me it's like lnwire.ExtraOpaqueData but specifically for address fields.

Also didn't know we support more addr types in addition to the five defined in bolt7.

We (as in LND) don't. but we should: SHOULD ignore the first address descriptor that does NOT match the types defined above. ie, we should still persist & propagate these. Here is an example of CLN adding new Websocket addresses as type 6.

Payload: payload,
}

default:
return nil, ErrUnknownAddressType
}
Expand All @@ -215,6 +262,8 @@ func SerializeAddr(w io.Writer, address net.Addr) error {
return encodeTCPAddr(w, addr)
case *tor.OnionAddr:
return encodeOnionAddr(w, addr)
case *lnwire.OpaqueAddrs:
return encodeOpaqueAddrs(w, addr)
default:
return ErrUnknownAddressType
}
Expand Down
53 changes: 53 additions & 0 deletions graph/db/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4063,3 +4063,56 @@ func TestClosedScid(t *testing.T) {
require.Nil(t, err)
require.True(t, exists)
}

// testNodeAnn is a serialized node announcement message which contains an
// address type (6) that LND is not aware of.
var testNodeAnn = "01012674c2e7ef68c73a086b7de2603f4ef1567358df84bb4edaa06c" +
"f2132965b14e2434faab04170f0089216accbd79188fa3d40dbb0438bd89782cae" +
"27cc656bf60007800088082a69a2625e7a2a024b9a1fa8e006f1e3937f65f66c40" +
"8e6da8e1ca728ea43222a7381df1cc449605024b9a424c554549524f4e2d76302e" +
"31312e307263332d362d67663963613934650000001d0180c7caa8260702240061" +
"80000000d0000000005cd2a001260706204c"
Comment thread
ellemouton marked this conversation as resolved.

// TestLightningNodePersistence takes a raw serialized node announcement
// message, converts it to our internal models.LightningNode type, persists it
// to disk, reads it again and converts it back to a wire message and asserts
// that the two messages are equal.
func TestLightningNodePersistence(t *testing.T) {
t.Parallel()

// Create a new test graph instance.
graph, err := MakeTestGraph(t)
require.NoError(t, err)

nodeAnnBytes, err := hex.DecodeString(testNodeAnn)
require.NoError(t, err)

// Use the raw serialized node announcement message create an
// lnwire.NodeAnnouncement instance.
msg, err := lnwire.ReadMessage(bytes.NewBuffer(nodeAnnBytes), 0)
require.NoError(t, err)
na, ok := msg.(*lnwire.NodeAnnouncement)
require.True(t, ok)

// Convert the wire message to our internal node representation.
node := models.NodeFromWireAnnouncement(na)

// Persist the node to disk.
err = graph.AddLightningNode(node)
require.NoError(t, err)

// Read the node from disk.
diskNode, err := graph.FetchLightningNode(node.PubKeyBytes)
require.NoError(t, err)

// Convert it back to a wire message.
wireMsg, err := diskNode.NodeAnnouncement(true)
require.NoError(t, err)

// Encode it and compare against the original.
var b bytes.Buffer
_, err = lnwire.WriteMessage(&b, wireMsg, 0)
require.NoError(t, err)

require.Equal(t, nodeAnnBytes, b.Bytes())
}
19 changes: 19 additions & 0 deletions graph/db/models/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,22 @@ func (l *LightningNode) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement,

return nodeAnn, nil
}

// NodeFromWireAnnouncement creates a LightningNode instance from an
// lnwire.NodeAnnouncement message.
func NodeFromWireAnnouncement(msg *lnwire.NodeAnnouncement) *LightningNode {
Comment thread
ellemouton marked this conversation as resolved.
timestamp := time.Unix(int64(msg.Timestamp), 0)
features := lnwire.NewFeatureVector(msg.Features, lnwire.Features)

return &LightningNode{
HaveNodeAnnouncement: true,
LastUpdate: timestamp,
Addresses: msg.Addresses,
PubKeyBytes: msg.NodeID,
Alias: msg.Alias.String(),
AuthSigBytes: msg.Signature.ToSignatureBytes(),
Features: features,
Color: msg.RGBColor,
ExtraOpaqueData: msg.ExtraOpaqueData,
}
}
4 changes: 2 additions & 2 deletions lnwire/lnwire_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,8 @@ func TestChanUpdateChanFlags(t *testing.T) {
}
}

// TestDecodeUnknownAddressType shows that an unknown address type is currently
// incorrectly dealt with.
// TestDecodeUnknownAddressType shows that an unknown address type is correctly
// decoded and encoded.
func TestDecodeUnknownAddressType(t *testing.T) {
// Add a normal, clearnet address.
tcpAddr := &net.TCPAddr{
Expand Down