Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lnwire: Update NodeAnnouncement address advertisement to match spec #154

Merged
merged 5 commits into from Mar 29, 2017

Conversation

bryanvu
Copy link
Contributor

@bryanvu bryanvu commented Feb 27, 2017

This PR should address issues #131, #135, and #141.

@@ -1142,7 +1142,7 @@ func (c *ChannelGraph) NewChannelEdge() *ChannelEdge {

func putLightningNode(nodeBucket *bolt.Bucket, aliasBucket *bolt.Bucket, node *LightningNode) error {
var (
scratch [8]byte
scratch [16]byte
Copy link
Contributor

Choose a reason for hiding this comment

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

Why it was changed to 16?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@AndrewSamokhvalov thanks for the review and the comments! Good catch here. I made a change that required the additional bytes, and then backed that out and left this in. Fixed now.

@@ -1199,27 +1209,45 @@ func fetchLightningNode(nodeBucket *bolt.Bucket,
func deserializeLightningNode(r io.Reader) (*LightningNode, error) {
node := &LightningNode{}

var scratch [8]byte
if _, err := r.Read(scratch[:]); err != nil {
var scratch [16]byte
Copy link
Contributor

Choose a reason for hiding this comment

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

the same


// 158
return length
// Base size, 147, but can be variable due to multiple addresses
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it is not so important as far as it will change in near future , but it seems for me that base size should be 140 because length of addresses might be equal to 0 according to spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.

t.Fatalf("payload length estimate is incorrect: expected %v "+
"got %v", serializedLength, na.MaxPayloadLength(0))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe rather than removing it, we consider check the exact length of message instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Re-added. I saw that a few of the other variable length messages had the payload length test taken out, but I think it makes sense to have it.

server.go Outdated
Permanent: true,
}
s.persistentConnReqs[pubStr] = connReq
go s.connMgr.Connect(connReq)
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we should think more about this logic:

  1. We are rewriting the connection request on every iteration now:(s.persistentConnReqs[pubStr] = connReq).
  2. I am not sure but it seems for me that such approach might lead to duplicate creation of peers. Maybe we should add additional check in addPeer method (server.go:559-560)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a slice of connReqs to address issue #1. For #2, I believe @Roasbeef was going to add something in the connection manager to ensure that when a connection is established, the other connection attempts are cancelled. I believe this should prevent the [inbound|outbound]peerConnected from being called more than once, ensuring that duplicate peers won't be created.

@bryanvu bryanvu force-pushed the nodeannounce branch 2 times, most recently from 83e2821 to a874ce9 Compare February 28, 2017 02:20
@andrewshvv
Copy link
Contributor

LGTM! 👍

return err
}

for _, address := range node.Addresses {
if err := wire.WriteVarString(&b, 0, address.Network()); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

iirc whether an address is IPv4 or IPv6 it'll always return a "tcp" if it's a net.TCPAddr. Therefore, instead I think we can just rely on the String method of the net.Addr itself here. It would also be a bit more compact (as we can leave out the network).

address, err := net.ResolveTCPAddr(network, addr)
if err != nil {
return nil, err
}
Copy link
Member

Choose a reason for hiding this comment

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

In light of my comment above, I think we should instead add a single byte prefix before the encoded address so we know how to properly decode it.

Also the address resolution above wouldn't work with say, .onion addresses.

lnwire/lnwire.go Outdated
if _, err := w.Write(ip[:]); err != nil {
return err
}
switch net := e.Network(); net {
Copy link
Member

Choose a reason for hiding this comment

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

This won't work as expected for IPv6 addresses as they'll still always return "tcp" from the Network() method.

I think instead, we might be able to use the To4 method on the IP struct to discern if an address if IPv6 or not. The method will return nil if the address an IPv6 address.

@@ -56,7 +56,10 @@ var (
someSig, _ = btcec.ParseSignature(sigStr, btcec.S256())
someSigBytes = someSig.Serialize()

someAddress = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
//TODO(bvu): one of the following addresses should be an IPv6 address.
Copy link
Member

Choose a reason for hiding this comment

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

This TODO should be addressed before we merge this PR.

@@ -109,10 +109,12 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
// We'll use this fake address for the IP address of all the nodes in
// our tests. This value isn't needed for path finding so it doesn't
// need to be unique.
testAddrs := make([]net.Addr, 0)
Copy link
Member

Choose a reason for hiding this comment

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

Style nit, this can instead be declared as:

var testAddrs []net.Addr

server.go Outdated
connReq := &connmgr.ConnReq{
Addr: lnAddr,
Permanent: true,
// In case a node has multiple addresses, attempt to connect to
Copy link
Member

Choose a reason for hiding this comment

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

A new line should be inserted above this comment.

server.go Outdated
}
delete(s.persistentConnReqs, pubStr)
Copy link
Member

Choose a reason for hiding this comment

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

The deletion should be moved into the if ... ok clause.

server.go Outdated
// ongoing connection attempts to ensure that we don't end up with a
// duplicate connecting to the same peer.
s.pendingConnMtx.RLock()
delete(s.persistentConnReqs, pubStr)
Copy link
Member

@Roasbeef Roasbeef Mar 1, 2017

Choose a reason for hiding this comment

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

Upon further consideration, I don't think deleting items from the persistentConnReqs map is required after all. Honestly, I think the management of the peer connection state needs a bit of a re-write...

@bryanvu bryanvu force-pushed the nodeannounce branch 2 times, most recently from be4f621 to 3e95cd9 Compare March 3, 2017 05:28
@bryanvu
Copy link
Contributor Author

bryanvu commented Mar 3, 2017

Updated. Previous version can be found at: https://github.com/bryanvu/lnd/tree/nodeannounce-1. Not sure the way I did the constants for TCP/ONION is idiomatic in Go, suggestions welcome.

@@ -111,6 +111,12 @@ type ChannelGraph struct {
// * LRU cache for edges?
}

// TODO(bvu): add comments
Copy link
Member

Choose a reason for hiding this comment

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

We can add a bit more typing to this definition with something along the lines of:

type addressType uint8

const (
    tcp4Addr addressType = 0
    tcp6Addr addressType = 1
    onionAddr addressType = 2

)

for _, address := range node.Addresses {
if address.Network() == "tcp" {
scratch[0] = byte(TCP)
if _, err := b.Write(scratch[0:0]); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

scratch[0:0] can safely be replaced with scratch[0]

var address net.Addr
switch addrType {
case TCP:
address, err = net.ResolveTCPAddr("tcp", addr)
Copy link
Member

Choose a reason for hiding this comment

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

As is now, this'll force an outbound name resolution for each address on db read which should be able to be avoided.

We can avoid this by writing the raw byte representation of a TCPAddr rather than using the supplied .String() method. So this would entail writing the raw bytes of the IP, Zone and Port fields.

lnd_test.go Outdated
for _, node := range chanGraph.Nodes {
if node.PubKey == dave.PubKeyStr {
if len(node.Addresses) != 3 {
t.Fatalf("expecting dave to have two addresses, got: %v",
Copy link
Member

Choose a reason for hiding this comment

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

Looks the the error message wasn't updated when the additional addresses were added (reads "two", should be "three").

}

chanAmt := btcutil.Amount(btcutil.SatoshiPerBitcoin / 2)
chanPointAlice := openChannelAndAssert(ctxt, t, net, net.Alice, dave,
Copy link
Member

Choose a reason for hiding this comment

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

I don't think opening a new channel is necessary for this test as it only deals with the proper propagation of a node announcement within the network.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since I was using DescribeGraph to check the contents of the NodeAnnouncement, I had to open a channel here, I believe.

copy(ip[:], e.IP.To16())
if _, err := w.Write(ip[:]); err != nil {
return err
}
Copy link
Member

Choose a reason for hiding this comment

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

Hmm just realized the spec currently leaves out the concept of IPv6 zones...

@bryanvu bryanvu force-pushed the nodeannounce branch 6 times, most recently from ad66bb2 to cae47da Compare March 20, 2017 18:26
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 67.35% when pulling cae47da on bryanvu:nodeannounce into 5d43483 on lightningnetwork:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 67.35% when pulling cae47da on bryanvu:nodeannounce into 5d43483 on lightningnetwork:master.

@bryanvu
Copy link
Contributor Author

bryanvu commented Mar 20, 2017

Updated with requested changes. Previous version can be found at: https://github.com/bryanvu/lnd/tree/nodeannounce-2.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 67.35% when pulling 9c9add9 on bryanvu:nodeannounce into 5d43483 on lightningnetwork:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.3%) to 67.46% when pulling 8162a66 on bryanvu:nodeannounce into 5d43483 on lightningnetwork:master.

@coveralls
Copy link

coveralls commented Mar 20, 2017

Coverage Status

Coverage decreased (-0.07%) to 67.648% when pulling db803b8 on bryanvu:nodeannounce into 5d43483 on lightningnetwork:master.

@@ -819,6 +830,8 @@ type LightningNode struct {
//
// TODO(roasbeef): hook into serialization once full verification is in
AuthSig *btcec.Signature
// Features is the list of protocol features supported by this node.
Copy link
Member

Choose a reason for hiding this comment

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

Minor nit: missing a newline above this variable definition.

for _, address := range node.Addresses {
if address.Network() == "tcp" {
if address.(*net.TCPAddr).IP.To4() != nil {
scratch[0] = 0
Copy link
Member

Choose a reason for hiding this comment

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

To improve the readability, I think the enum defined above should be used directly here:

scratch[0] = tcp4Addr

return err
}
} else {
scratch[0] = 1
Copy link
Member

Choose a reason for hiding this comment

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

Similar comment here about using the enum directly.

return err
}

copy(scratch[:4], address.(*net.TCPAddr).IP.To4())
Copy link
Member

Choose a reason for hiding this comment

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

From my reading, this looks like it'll override the address type which is the first element in the slice. To fix this, the bounds on this write should instead be:

scratch[1:5]

if _, err := b.Write(scratch[:1]); err != nil {
return err
}
copy(scratch[:], address.(*net.TCPAddr).IP.To16())
Copy link
Member

Choose a reason for hiding this comment

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

Similar comment here about the copy overriding the addrType set as the first variable.

lnd_test.go Outdated

for _, node := range chanGraph.Nodes {
if node.PubKey == dave.PubKeyStr {
if len(node.Addresses) != 3 {
Copy link
Member

Choose a reason for hiding this comment

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

For the sake of additional coverage, I think this test should also be expanded to assert the exact existence of the three addresses listed above.

Addresses []net.Addr

// pad is used to reserve to additional bytes for future usage.
pad uint16
Copy link
Member

Choose a reason for hiding this comment

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

Why was this padding added? It isn't a part of the struct.

You can also remove the TODO(roasbeef) above.

if connReq, ok := s.persistentConnReqs[pubStr]; ok {
s.connMgr.Remove(connReq.ID())
if connReqs, ok := s.persistentConnReqs[pubStr]; ok {
for _, connReq := range connReqs {
Copy link
Member

Choose a reason for hiding this comment

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

A similar code block (removing all the connection requests for the particular node pubkey) needs to be inserted below within the outboundPeerConnected method. We'll need to similarly cancel all the pending outbound requests (launched within newServer) as we've successfully established a connection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not totally sure how this works, but when I added this to outboundPeerConnected, the nodes didn't reconnect upon startup. Is there something else I have to do to make that happen?

lnwire/lnwire.go Outdated
}
if e.IP.To4() != nil {
var descriptor [1]byte
descriptor[0] = 1
Copy link
Member

Choose a reason for hiding this comment

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

Can you lift the 1 and 2 values, into an addrType enum similar to what was done in the channeldb package? Thanks!

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

Meant to request changes rather than approve...

@bryanvu bryanvu force-pushed the nodeannounce branch 2 times, most recently from 8651277 to 57ec79a Compare March 22, 2017 23:18
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.03%) to 68.995% when pulling 57ec79a on bryanvu:nodeannounce into 1efbaee on lightningnetwork:master.

This commit modifies address handling in the NodeAnnouncement struct,
switching from net.TCPAddr to []net.Addr. This enables more flexible
address handling with multiple types and multiple addresses for each
node. This commit addresses the first part of issue lightningnetwork#131 .
Minor change to server.go to add ExternalIPs to
channeldb.LightningNode. Also, added a test that utilizes this
functionality and exercises multiple addresses in NodeAnnouncement.
Add support for Features in NodeAnnouncment according to spec.
Use addresses and ports from NodeAnnouncement messages for reconnection
attempts. For those nodes that don't explicitly report IP addresses, use
the IP address from previous connections connection request along with
the default peer port number.
@coveralls
Copy link

coveralls commented Mar 29, 2017

Coverage Status

Coverage decreased (-0.03%) to 69.01% when pulling 8d4f94d on bryanvu:nodeannounce into 65c15c4 on lightningnetwork:master.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

Alright, I've run some final tests locally and everything seems to be in order. This directly and indirectly addresses a number of lingering issues and opens the door to allow us to do things like experiment with automated channel creation with a fixed pool of funds for a node.

LGTM 🏂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants