lnwire: add support for Features in NodeAnnouncement
Add support for Features in NodeAnnouncment according to spec.
This commit is contained in:
parent
3087bec4db
commit
085b7333cb
@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
@ -830,6 +831,9 @@ 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.
|
||||
Features *lnwire.FeatureVector
|
||||
|
||||
db *DB
|
||||
|
||||
// TODO(roasbeef): discovery will need storage to keep it's last IP
|
||||
@ -1309,6 +1313,10 @@ func putLightningNode(nodeBucket *bolt.Bucket, aliasBucket *bolt.Bucket, node *L
|
||||
return err
|
||||
}
|
||||
|
||||
if err := node.Features.Encode(&b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numAddresses := uint16(len(node.Addresses))
|
||||
byteOrder.PutUint16(scratch[:2], numAddresses)
|
||||
if _, err := b.Write(scratch[:2]); err != nil {
|
||||
@ -1395,6 +1403,11 @@ func deserializeLightningNode(r io.Reader) (*LightningNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node.Features, err = lnwire.NewFeatureVectorFromReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := r.Read(scratch[:2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
@ -35,6 +36,8 @@ var (
|
||||
}
|
||||
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
|
||||
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10)
|
||||
|
||||
testFeatures = lnwire.NewFeatureVector([]lnwire.Feature{})
|
||||
)
|
||||
|
||||
func createTestVertex(db *DB) (*LightningNode, error) {
|
||||
@ -48,10 +51,11 @@ func createTestVertex(db *DB) (*LightningNode, error) {
|
||||
pub := priv.PubKey().SerializeCompressed()
|
||||
return &LightningNode{
|
||||
LastUpdate: time.Unix(updateTime, 0),
|
||||
Addresses: testAddrs,
|
||||
PubKey: priv.PubKey(),
|
||||
Color: color.RGBA{1, 2, 3, 0},
|
||||
Alias: "kek" + string(pub[:]),
|
||||
Features: testFeatures,
|
||||
Addresses: testAddrs,
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
@ -70,10 +74,11 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
|
||||
_, testPub := btcec.PrivKeyFromBytes(btcec.S256(), key[:])
|
||||
node := &LightningNode{
|
||||
LastUpdate: time.Unix(1232342, 0),
|
||||
Addresses: testAddrs,
|
||||
PubKey: testPub,
|
||||
Color: color.RGBA{1, 2, 3, 0},
|
||||
Alias: "kek",
|
||||
Features: testFeatures,
|
||||
Addresses: testAddrs,
|
||||
db: db,
|
||||
}
|
||||
|
||||
@ -97,9 +102,8 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
|
||||
}
|
||||
|
||||
// The two nodes should match exactly!
|
||||
if !reflect.DeepEqual(node, dbNode) {
|
||||
t.Fatalf("retrieved node doesn't match: expected %#v\n, got %#v\n",
|
||||
node, dbNode)
|
||||
if err := compareNodes(node, dbNode); err != nil {
|
||||
t.Fatalf("nodes don't match: %v", err)
|
||||
}
|
||||
|
||||
// Next, delete the node from the graph, this should purge all data
|
||||
@ -194,9 +198,8 @@ func TestSourceNode(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unable to fetch source node: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testNode, sourceNode) {
|
||||
t.Fatalf("nodes don't match, expected %#v \n got %#v",
|
||||
testNode, sourceNode)
|
||||
if err := compareNodes(testNode, sourceNode); err != nil {
|
||||
t.Fatalf("nodes don't match: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,13 +457,11 @@ func TestEdgeInfoUpdates(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unable to fetch channel by ID: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(dbEdge1, edge1) {
|
||||
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge1,
|
||||
dbEdge1)
|
||||
if err := compareEdgePolicies(dbEdge1, edge1); err != nil {
|
||||
t.Fatalf("edge doesn't match: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(dbEdge2, edge2) {
|
||||
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge2,
|
||||
dbEdge2)
|
||||
if err := compareEdgePolicies(dbEdge2, edge2); err != nil {
|
||||
t.Fatalf("edge doesn't match: %v", err)
|
||||
}
|
||||
assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo)
|
||||
|
||||
@ -470,13 +471,11 @@ func TestEdgeInfoUpdates(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unable to fetch channel by ID: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(dbEdge1, edge1) {
|
||||
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge1,
|
||||
dbEdge1)
|
||||
if err := compareEdgePolicies(dbEdge1, edge1); err != nil {
|
||||
t.Fatalf("edge doesn't match: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(dbEdge2, edge2) {
|
||||
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge2,
|
||||
dbEdge2)
|
||||
if err := compareEdgePolicies(dbEdge2, edge2); err != nil {
|
||||
t.Fatalf("edge doesn't match: %v", err)
|
||||
}
|
||||
assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo)
|
||||
}
|
||||
@ -830,3 +829,77 @@ func TestGraphPruning(t *testing.T) {
|
||||
assertPruneTip(t, graph, &blockHash, blockHeight)
|
||||
asserNumChans(t, graph, 0)
|
||||
}
|
||||
|
||||
// compareNodes is used to compare two LightningNodes while excluding the
|
||||
// Features struct, which cannot be compared as the semantics for reserializing
|
||||
// the featuresMap have not been defined.
|
||||
func compareNodes(a, b *LightningNode) error {
|
||||
if !reflect.DeepEqual(a.LastUpdate, b.LastUpdate) {
|
||||
return fmt.Errorf("LastUpdate doesn't match: expected %#v, \n"+
|
||||
"got %#v", a.LastUpdate, b.LastUpdate)
|
||||
}
|
||||
if !reflect.DeepEqual(a.Addresses, b.Addresses) {
|
||||
return fmt.Errorf("Addresses doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.Addresses, b.Addresses)
|
||||
}
|
||||
if !reflect.DeepEqual(a.PubKey, b.PubKey) {
|
||||
return fmt.Errorf("PubKey doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.PubKey, b.PubKey)
|
||||
}
|
||||
if !reflect.DeepEqual(a.Color, b.Color) {
|
||||
return fmt.Errorf("Color doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.Color, b.Color)
|
||||
}
|
||||
if !reflect.DeepEqual(a.Alias, b.Alias) {
|
||||
return fmt.Errorf("Alias doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.Alias, b.Alias)
|
||||
}
|
||||
if !reflect.DeepEqual(a.db, b.db) {
|
||||
return fmt.Errorf("db doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.db, b.db)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// compareEdgePolicies is used to compare two ChannelEdgePolices using
|
||||
// compareNodes, so as to exclude comparisons of the Nodes' Features struct.
|
||||
func compareEdgePolicies(a, b *ChannelEdgePolicy) error {
|
||||
if a.ChannelID != b.ChannelID {
|
||||
return fmt.Errorf("ChannelID doesn't match: expected %v, "+
|
||||
"got %v", a.ChannelID, b.ChannelID)
|
||||
}
|
||||
if !reflect.DeepEqual(a.LastUpdate, b.LastUpdate) {
|
||||
return fmt.Errorf("LastUpdate doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.LastUpdate, b.LastUpdate)
|
||||
}
|
||||
if a.Flags != b.Flags {
|
||||
return fmt.Errorf("Flags doesn't match: expected %v, "+
|
||||
"got %v", a.Flags, b.Flags)
|
||||
}
|
||||
if a.TimeLockDelta != b.TimeLockDelta {
|
||||
return fmt.Errorf("TimeLockDelta doesn't match: expected %v, "+
|
||||
"got %v", a.TimeLockDelta, b.TimeLockDelta)
|
||||
}
|
||||
if a.MinHTLC != b.MinHTLC {
|
||||
return fmt.Errorf("MinHTLC doesn't match: expected %v, "+
|
||||
"got %v", a.MinHTLC, b.MinHTLC)
|
||||
}
|
||||
if a.FeeBaseMSat != b.FeeBaseMSat {
|
||||
return fmt.Errorf("FeeBaseMSat doesn't match: expected %v, "+
|
||||
"got %v", a.FeeBaseMSat, b.FeeBaseMSat)
|
||||
}
|
||||
if a.FeeProportionalMillionths != b.FeeProportionalMillionths {
|
||||
return fmt.Errorf("FeeProportionalMillionths doesn't match: "+
|
||||
"expected %v, got %v", a.FeeProportionalMillionths,
|
||||
b.FeeProportionalMillionths)
|
||||
}
|
||||
if err := compareNodes(a.Node, b.Node); err != nil {
|
||||
return err
|
||||
}
|
||||
if !reflect.DeepEqual(a.db, b.db) {
|
||||
return fmt.Errorf("db doesn't match: expected %#v, \n "+
|
||||
"got %#v", a.db, b.db)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2084,7 +2084,7 @@ func testNodeAnnouncement(net *networkHarness, t *harnessTest) {
|
||||
}
|
||||
|
||||
var lndArgs []string
|
||||
for address, _ := range ipAddresses {
|
||||
for address := range ipAddresses {
|
||||
lndArgs = append(lndArgs, "--externalip="+address)
|
||||
}
|
||||
|
||||
|
@ -75,4 +75,9 @@ var (
|
||||
green: 255,
|
||||
blue: 255,
|
||||
}
|
||||
|
||||
someFeature = featureName("somefeature")
|
||||
someFeatures = NewFeatureVector([]Feature{
|
||||
{someFeature, OptionalFlag},
|
||||
})
|
||||
)
|
||||
|
@ -97,6 +97,9 @@ type NodeAnnouncement struct {
|
||||
// Alias is used to customize their node's appearance in maps and graphs
|
||||
Alias Alias
|
||||
|
||||
// Features is the list of protocol features this node supports.
|
||||
Features *FeatureVector
|
||||
|
||||
// Address includes two specification fields: 'ipv6' and 'port' on which
|
||||
// the node is accepting incoming connections.
|
||||
Addresses []net.Addr
|
||||
@ -127,6 +130,7 @@ func (a *NodeAnnouncement) Decode(r io.Reader, pver uint32) error {
|
||||
&a.RGBColor,
|
||||
&a.Alias,
|
||||
&a.Addresses,
|
||||
&a.Features,
|
||||
)
|
||||
}
|
||||
|
||||
@ -141,6 +145,7 @@ func (a *NodeAnnouncement) Encode(w io.Writer, pver uint32) error {
|
||||
a.RGBColor,
|
||||
a.Alias,
|
||||
a.Addresses,
|
||||
a.Features,
|
||||
)
|
||||
}
|
||||
|
||||
@ -162,6 +167,7 @@ func (a *NodeAnnouncement) MaxPayloadLength(pver uint32) uint32 {
|
||||
// NodeID - 33 bytes
|
||||
// RGBColor - 3 bytes
|
||||
// Alias - 32 bytes
|
||||
// Features - variable
|
||||
// NumAddresses - 2 bytes
|
||||
// AddressDescriptor - 1 byte
|
||||
// Ipv4 - 4 bytes (optional)
|
||||
@ -183,6 +189,7 @@ func (a *NodeAnnouncement) DataToSign() ([]byte, error) {
|
||||
a.RGBColor,
|
||||
a.Alias,
|
||||
a.Addresses,
|
||||
a.Features,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -10,31 +10,37 @@ import (
|
||||
)
|
||||
|
||||
func TestNodeAnnouncementEncodeDecode(t *testing.T) {
|
||||
cua := &NodeAnnouncement{
|
||||
na := &NodeAnnouncement{
|
||||
Signature: someSig,
|
||||
Timestamp: maxUint32,
|
||||
NodeID: pubKey,
|
||||
RGBColor: someRGB,
|
||||
Alias: someAlias,
|
||||
Addresses: someAddresses,
|
||||
Features: someFeatures,
|
||||
}
|
||||
|
||||
// Next encode the NA message into an empty bytes buffer.
|
||||
var b bytes.Buffer
|
||||
if err := cua.Encode(&b, 0); err != nil {
|
||||
if err := na.Encode(&b, 0); err != nil {
|
||||
t.Fatalf("unable to encode NodeAnnouncement: %v", err)
|
||||
}
|
||||
|
||||
// Deserialize the encoded NA message into a new empty struct.
|
||||
cua2 := &NodeAnnouncement{}
|
||||
if err := cua2.Decode(&b, 0); err != nil {
|
||||
na2 := &NodeAnnouncement{}
|
||||
if err := na2.Decode(&b, 0); err != nil {
|
||||
t.Fatalf("unable to decode NodeAnnouncement: %v", err)
|
||||
}
|
||||
|
||||
// We do not encode the feature map in feature vector, for that reason
|
||||
// the node announcement messages will differ. Set feature map with nil
|
||||
// in order to use deep equal function.
|
||||
na.Features.featuresMap = nil
|
||||
|
||||
// Assert equality of the two instances.
|
||||
if !reflect.DeepEqual(cua, cua2) {
|
||||
if !reflect.DeepEqual(na, na2) {
|
||||
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
|
||||
cua, cua2)
|
||||
na, na2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +58,7 @@ func TestNodeAnnouncementValidation(t *testing.T) {
|
||||
NodeID: nodePubKey,
|
||||
RGBColor: someRGB,
|
||||
Alias: someAlias,
|
||||
Features: someFeatures,
|
||||
}
|
||||
|
||||
dataToSign, _ := na.DataToSign()
|
||||
@ -73,6 +80,7 @@ func TestNodeAnnouncementPayloadLength(t *testing.T) {
|
||||
RGBColor: someRGB,
|
||||
Alias: someAlias,
|
||||
Addresses: someAddresses,
|
||||
Features: someFeatures,
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
@ -81,9 +89,9 @@ func TestNodeAnnouncementPayloadLength(t *testing.T) {
|
||||
}
|
||||
|
||||
serializedLength := uint32(b.Len())
|
||||
if serializedLength != 164 {
|
||||
if serializedLength != 167 {
|
||||
t.Fatalf("payload length estimate is incorrect: expected %v "+
|
||||
"got %v", 164, serializedLength)
|
||||
"got %v", 167, serializedLength)
|
||||
}
|
||||
|
||||
if na.MaxPayloadLength(0) != 8192 {
|
||||
|
@ -24,6 +24,8 @@ var (
|
||||
Port: 9000}
|
||||
testAddrs = []net.Addr{testAddr}
|
||||
|
||||
testFeatures = lnwire.NewFeatureVector([]lnwire.Feature{})
|
||||
|
||||
testHash = [32]byte{
|
||||
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
|
||||
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
|
||||
@ -47,6 +49,7 @@ func createGraphNode() (*channeldb.LightningNode, error) {
|
||||
PubKey: priv.PubKey(),
|
||||
Color: color.RGBA{1, 2, 3, 0},
|
||||
Alias: "kek" + string(pub[:]),
|
||||
Features: testFeatures,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -68,6 +71,7 @@ func createTestWireNode() (*lnwire.NodeAnnouncement, error) {
|
||||
Addresses: testAddrs,
|
||||
NodeID: priv.PubKey(),
|
||||
Alias: alias,
|
||||
Features: testFeatures,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,7 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
|
||||
Addresses: testAddrs,
|
||||
PubKey: pub,
|
||||
Alias: node.Alias,
|
||||
Features: testFeatures,
|
||||
}
|
||||
|
||||
// We require all aliases within the graph to be unique for our
|
||||
|
@ -696,6 +696,7 @@ func (r *ChannelRouter) processNetworkAnnouncement(msg lnwire.Message) bool {
|
||||
Addresses: msg.Addresses,
|
||||
PubKey: msg.NodeID,
|
||||
Alias: msg.Alias.String(),
|
||||
Features: msg.Features,
|
||||
}
|
||||
|
||||
if err = r.cfg.Graph.AddLightningNode(node); err != nil {
|
||||
@ -945,9 +946,10 @@ func (r *ChannelRouter) syncChannelGraph(syncReq *syncRequest) error {
|
||||
ann := &lnwire.NodeAnnouncement{
|
||||
Signature: r.fakeSig,
|
||||
Timestamp: uint32(node.LastUpdate.Unix()),
|
||||
Addresses: node.Addresses,
|
||||
NodeID: node.PubKey,
|
||||
Alias: alias,
|
||||
Features: node.Features,
|
||||
Addresses: node.Addresses,
|
||||
}
|
||||
announceMessages = append(announceMessages, ann)
|
||||
|
||||
|
@ -171,7 +171,8 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
Addresses: selfAddrs,
|
||||
PubKey: privKey.PubKey(),
|
||||
// TODO(roasbeef): make alias configurable
|
||||
Alias: hex.EncodeToString(serializedPubKey[:10]),
|
||||
Alias: hex.EncodeToString(serializedPubKey[:10]),
|
||||
Features: globalFeatures,
|
||||
}
|
||||
if err := chanGraph.SetSourceNode(self); err != nil {
|
||||
return nil, err
|
||||
@ -510,7 +511,6 @@ func (s *server) inboundPeerConnected(conn net.Conn) {
|
||||
for _, connReq := range connReqs {
|
||||
s.connMgr.Remove(connReq.ID())
|
||||
}
|
||||
delete(s.persistentConnReqs, pubStr)
|
||||
}
|
||||
s.pendingConnMtx.RUnlock()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user