Skip to content

Commit db803b8

Browse files
committed
lnwire: add support for Features in NodeAnnouncement
Add support for Features in NodeAnnouncment according to spec.
1 parent be1b33f commit db803b8

9 files changed

+148
-36
lines changed

channeldb/graph.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/boltdb/bolt"
12+
"github.com/lightningnetwork/lnd/lnwire"
1213
"github.com/roasbeef/btcd/btcec"
1314
"github.com/roasbeef/btcd/chaincfg/chainhash"
1415
"github.com/roasbeef/btcd/wire"
@@ -829,6 +830,8 @@ type LightningNode struct {
829830
//
830831
// TODO(roasbeef): hook into serialization once full verification is in
831832
AuthSig *btcec.Signature
833+
// Features is the list of protocol features supported by this node.
834+
Features *lnwire.FeatureVector
832835

833836
db *DB
834837

@@ -1309,6 +1312,10 @@ func putLightningNode(nodeBucket *bolt.Bucket, aliasBucket *bolt.Bucket, node *L
13091312
return err
13101313
}
13111314

1315+
if err := node.Features.Encode(&b); err != nil {
1316+
return err
1317+
}
1318+
13121319
numAddresses := uint16(len(node.Addresses))
13131320
byteOrder.PutUint16(scratch[:2], numAddresses)
13141321
if _, err := b.Write(scratch[:2]); err != nil {
@@ -1396,6 +1403,11 @@ func deserializeLightningNode(r io.Reader) (*LightningNode, error) {
13961403
return nil, err
13971404
}
13981405

1406+
node.Features, err = lnwire.NewFeatureVectorFromReader(r)
1407+
if err != nil {
1408+
return nil, err
1409+
}
1410+
13991411
if _, err := r.Read(scratch[:2]); err != nil {
14001412
return nil, err
14011413
}

channeldb/graph_test.go

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"time"
1515

1616
"github.com/davecgh/go-spew/spew"
17+
"github.com/lightningnetwork/lnd/lnwire"
1718
"github.com/roasbeef/btcd/btcec"
1819
"github.com/roasbeef/btcd/chaincfg/chainhash"
1920
"github.com/roasbeef/btcd/wire"
@@ -35,6 +36,8 @@ var (
3536
}
3637
_, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10)
3738
_, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10)
39+
40+
testFeatures = lnwire.NewFeatureVector([]lnwire.Feature{})
3841
)
3942

4043
func createTestVertex(db *DB) (*LightningNode, error) {
@@ -48,10 +51,11 @@ func createTestVertex(db *DB) (*LightningNode, error) {
4851
pub := priv.PubKey().SerializeCompressed()
4952
return &LightningNode{
5053
LastUpdate: time.Unix(updateTime, 0),
51-
Addresses: testAddrs,
5254
PubKey: priv.PubKey(),
5355
Color: color.RGBA{1, 2, 3, 0},
5456
Alias: "kek" + string(pub[:]),
57+
Features: testFeatures,
58+
Addresses: testAddrs,
5559
db: db,
5660
}, nil
5761
}
@@ -70,10 +74,11 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
7074
_, testPub := btcec.PrivKeyFromBytes(btcec.S256(), key[:])
7175
node := &LightningNode{
7276
LastUpdate: time.Unix(1232342, 0),
73-
Addresses: testAddrs,
7477
PubKey: testPub,
7578
Color: color.RGBA{1, 2, 3, 0},
7679
Alias: "kek",
80+
Features: testFeatures,
81+
Addresses: testAddrs,
7782
db: db,
7883
}
7984

@@ -97,9 +102,8 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
97102
}
98103

99104
// The two nodes should match exactly!
100-
if !reflect.DeepEqual(node, dbNode) {
101-
t.Fatalf("retrieved node doesn't match: expected %#v\n, got %#v\n",
102-
node, dbNode)
105+
if err := compareNodes(node, dbNode); err != nil {
106+
t.Fatalf("nodes don't match: %v", err)
103107
}
104108

105109
// Next, delete the node from the graph, this should purge all data
@@ -194,9 +198,8 @@ func TestSourceNode(t *testing.T) {
194198
if err != nil {
195199
t.Fatalf("unable to fetch source node: %v", err)
196200
}
197-
if !reflect.DeepEqual(testNode, sourceNode) {
198-
t.Fatalf("nodes don't match, expected %#v \n got %#v",
199-
testNode, sourceNode)
201+
if err := compareNodes(testNode, sourceNode); err != nil {
202+
t.Fatalf("nodes don't match: %v", err)
200203
}
201204
}
202205

@@ -454,13 +457,11 @@ func TestEdgeInfoUpdates(t *testing.T) {
454457
if err != nil {
455458
t.Fatalf("unable to fetch channel by ID: %v", err)
456459
}
457-
if !reflect.DeepEqual(dbEdge1, edge1) {
458-
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge1,
459-
dbEdge1)
460+
if err := compareEdgePolicies(dbEdge1, edge1); err != nil {
461+
t.Fatalf("edge doesn't match: %v", err)
460462
}
461-
if !reflect.DeepEqual(dbEdge2, edge2) {
462-
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge2,
463-
dbEdge2)
463+
if err := compareEdgePolicies(dbEdge2, edge2); err != nil {
464+
t.Fatalf("edge doesn't match: %v", err)
464465
}
465466
assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo)
466467

@@ -470,13 +471,11 @@ func TestEdgeInfoUpdates(t *testing.T) {
470471
if err != nil {
471472
t.Fatalf("unable to fetch channel by ID: %v", err)
472473
}
473-
if !reflect.DeepEqual(dbEdge1, edge1) {
474-
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge1,
475-
dbEdge1)
474+
if err := compareEdgePolicies(dbEdge1, edge1); err != nil {
475+
t.Fatalf("edge doesn't match: %v", err)
476476
}
477-
if !reflect.DeepEqual(dbEdge2, edge2) {
478-
t.Fatalf("edge doesn't match: expected %#v, \n got %#v", edge2,
479-
dbEdge2)
477+
if err := compareEdgePolicies(dbEdge2, edge2); err != nil {
478+
t.Fatalf("edge doesn't match: %v", err)
480479
}
481480
assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo)
482481
}
@@ -830,3 +829,77 @@ func TestGraphPruning(t *testing.T) {
830829
assertPruneTip(t, graph, &blockHash, blockHeight)
831830
asserNumChans(t, graph, 0)
832831
}
832+
833+
// compareNodes is used to compare two LightningNodes while excluding the
834+
// Features struct, which cannot be compared as the semantics for reserializing
835+
// the featuresMap have not been defined.
836+
func compareNodes(a, b *LightningNode) error {
837+
if !reflect.DeepEqual(a.LastUpdate, b.LastUpdate) {
838+
return fmt.Errorf("LastUpdate doesn't match: expected %#v, \n"+
839+
"got %#v", a.LastUpdate, b.LastUpdate)
840+
}
841+
if !reflect.DeepEqual(a.Addresses, b.Addresses) {
842+
return fmt.Errorf("Addresses doesn't match: expected %#v, \n "+
843+
"got %#v", a.Addresses, b.Addresses)
844+
}
845+
if !reflect.DeepEqual(a.PubKey, b.PubKey) {
846+
return fmt.Errorf("PubKey doesn't match: expected %#v, \n "+
847+
"got %#v", a.PubKey, b.PubKey)
848+
}
849+
if !reflect.DeepEqual(a.Color, b.Color) {
850+
return fmt.Errorf("Color doesn't match: expected %#v, \n "+
851+
"got %#v", a.Color, b.Color)
852+
}
853+
if !reflect.DeepEqual(a.Alias, b.Alias) {
854+
return fmt.Errorf("Alias doesn't match: expected %#v, \n "+
855+
"got %#v", a.Alias, b.Alias)
856+
}
857+
if !reflect.DeepEqual(a.db, b.db) {
858+
return fmt.Errorf("db doesn't match: expected %#v, \n "+
859+
"got %#v", a.db, b.db)
860+
}
861+
862+
return nil
863+
}
864+
865+
// compareEdgePolicies is used to compare two ChannelEdgePolices using
866+
// compareNodes, so as to exclude comparisons of the Nodes' Features struct.
867+
func compareEdgePolicies(a, b *ChannelEdgePolicy) error {
868+
if a.ChannelID != b.ChannelID {
869+
return fmt.Errorf("ChannelID doesn't match: expected %v, "+
870+
"got %v", a.ChannelID, b.ChannelID)
871+
}
872+
if !reflect.DeepEqual(a.LastUpdate, b.LastUpdate) {
873+
return fmt.Errorf("LastUpdate doesn't match: expected %#v, \n "+
874+
"got %#v", a.LastUpdate, b.LastUpdate)
875+
}
876+
if a.Flags != b.Flags {
877+
return fmt.Errorf("Flags doesn't match: expected %v, "+
878+
"got %v", a.Flags, b.Flags)
879+
}
880+
if a.TimeLockDelta != b.TimeLockDelta {
881+
return fmt.Errorf("TimeLockDelta doesn't match: expected %v, "+
882+
"got %v", a.TimeLockDelta, b.TimeLockDelta)
883+
}
884+
if a.MinHTLC != b.MinHTLC {
885+
return fmt.Errorf("MinHTLC doesn't match: expected %v, "+
886+
"got %v", a.MinHTLC, b.MinHTLC)
887+
}
888+
if a.FeeBaseMSat != b.FeeBaseMSat {
889+
return fmt.Errorf("FeeBaseMSat doesn't match: expected %v, "+
890+
"got %v", a.FeeBaseMSat, b.FeeBaseMSat)
891+
}
892+
if a.FeeProportionalMillionths != b.FeeProportionalMillionths {
893+
return fmt.Errorf("FeeProportionalMillionths doesn't match: "+
894+
"expected %v, got %v", a.FeeProportionalMillionths,
895+
b.FeeProportionalMillionths)
896+
}
897+
if err := compareNodes(a.Node, b.Node); err != nil {
898+
return err
899+
}
900+
if !reflect.DeepEqual(a.db, b.db) {
901+
return fmt.Errorf("db doesn't match: expected %#v, \n "+
902+
"got %#v", a.db, b.db)
903+
}
904+
return nil
905+
}

lnwire/lnwire_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,9 @@ var (
7575
green: 255,
7676
blue: 255,
7777
}
78+
79+
someFeature = featureName("somefeature")
80+
someFeatures = NewFeatureVector([]Feature{
81+
{someFeature, OptionalFlag},
82+
})
7883
)

lnwire/node_announcement.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ type NodeAnnouncement struct {
9999
// Alias is used to customize their node's appearance in maps and graphs
100100
Alias Alias
101101

102+
// Features is the list of protocol features this node supports.
103+
Features *FeatureVector
104+
102105
// Address includes two specification fields: 'ipv6' and 'port' on which
103106
// the node is accepting incoming connections.
104107
Addresses []net.Addr
@@ -132,6 +135,7 @@ func (a *NodeAnnouncement) Decode(r io.Reader, pver uint32) error {
132135
&a.RGBColor,
133136
&a.Alias,
134137
&a.Addresses,
138+
&a.Features,
135139
&a.pad,
136140
)
137141
}
@@ -147,6 +151,7 @@ func (a *NodeAnnouncement) Encode(w io.Writer, pver uint32) error {
147151
a.RGBColor,
148152
a.Alias,
149153
a.Addresses,
154+
a.Features,
150155
a.pad,
151156
)
152157
}
@@ -169,6 +174,7 @@ func (a *NodeAnnouncement) MaxPayloadLength(pver uint32) uint32 {
169174
// NodeID - 33 bytes
170175
// RGBColor - 3 bytes
171176
// Alias - 32 bytes
177+
// Features - variable
172178
// NumAddresses - 2 bytes
173179
// AddressDescriptor - 1 byte
174180
// Ipv4 - 4 bytes (optional)
@@ -191,6 +197,7 @@ func (a *NodeAnnouncement) DataToSign() ([]byte, error) {
191197
a.RGBColor,
192198
a.Alias,
193199
a.Addresses,
200+
a.Features,
194201
a.pad,
195202
)
196203
if err != nil {

lnwire/node_announcement_test.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,38 @@ import (
1010
)
1111

1212
func TestNodeAnnouncementEncodeDecode(t *testing.T) {
13-
cua := &NodeAnnouncement{
13+
na := &NodeAnnouncement{
1414
Signature: someSig,
1515
Timestamp: maxUint32,
16-
Addresses: someAddresses,
1716
NodeID: pubKey,
1817
RGBColor: someRGB,
19-
pad: maxUint16,
2018
Alias: someAlias,
19+
Features: someFeatures,
20+
Addresses: someAddresses,
21+
pad: maxUint16,
2122
}
2223

2324
// Next encode the NA message into an empty bytes buffer.
2425
var b bytes.Buffer
25-
if err := cua.Encode(&b, 0); err != nil {
26+
if err := na.Encode(&b, 0); err != nil {
2627
t.Fatalf("unable to encode NodeAnnouncement: %v", err)
2728
}
2829

2930
// Deserialize the encoded NA message into a new empty struct.
30-
cua2 := &NodeAnnouncement{}
31-
if err := cua2.Decode(&b, 0); err != nil {
31+
na2 := &NodeAnnouncement{}
32+
if err := na2.Decode(&b, 0); err != nil {
3233
t.Fatalf("unable to decode NodeAnnouncement: %v", err)
3334
}
3435

36+
// We do not encode the feature map in feature vector, for that reason
37+
// the node announcement messages will differ. Set feature map with nil
38+
// in order to use deep equal function.
39+
na.Features.featuresMap = nil
40+
3541
// Assert equality of the two instances.
36-
if !reflect.DeepEqual(cua, cua2) {
42+
if !reflect.DeepEqual(na, na2) {
3743
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
38-
cua, cua2)
44+
na, na2)
3945
}
4046
}
4147

@@ -52,8 +58,9 @@ func TestNodeAnnouncementValidation(t *testing.T) {
5258
Addresses: someAddresses,
5359
NodeID: nodePubKey,
5460
RGBColor: someRGB,
55-
pad: maxUint16,
5661
Alias: someAlias,
62+
Features: someFeatures,
63+
pad: maxUint16,
5764
}
5865

5966
dataToSign, _ := na.DataToSign()
@@ -71,11 +78,12 @@ func TestNodeAnnouncementPayloadLength(t *testing.T) {
7178
na := &NodeAnnouncement{
7279
Signature: someSig,
7380
Timestamp: maxUint32,
74-
Addresses: someAddresses,
7581
NodeID: pubKey,
7682
RGBColor: someRGB,
77-
pad: maxUint16,
7883
Alias: someAlias,
84+
Features: someFeatures,
85+
Addresses: someAddresses,
86+
pad: maxUint16,
7987
}
8088

8189
var b bytes.Buffer
@@ -84,9 +92,9 @@ func TestNodeAnnouncementPayloadLength(t *testing.T) {
8492
}
8593

8694
serializedLength := uint32(b.Len())
87-
if serializedLength != 166 {
95+
if serializedLength != 169 {
8896
t.Fatalf("payload length estimate is incorrect: expected %v "+
89-
"got %v", 166, serializedLength)
97+
"got %v", 169, serializedLength)
9098
}
9199

92100
if na.MaxPayloadLength(0) != 8192 {

routing/notifications_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ var (
2424
Port: 9000}
2525
testAddrs = []net.Addr{testAddr}
2626

27+
testFeatures = lnwire.NewFeatureVector([]lnwire.Feature{})
28+
2729
testHash = [32]byte{
2830
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
2931
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
@@ -47,6 +49,7 @@ func createGraphNode() (*channeldb.LightningNode, error) {
4749
PubKey: priv.PubKey(),
4850
Color: color.RGBA{1, 2, 3, 0},
4951
Alias: "kek" + string(pub[:]),
52+
Features: testFeatures,
5053
}, nil
5154
}
5255

@@ -68,6 +71,7 @@ func createTestWireNode() (*lnwire.NodeAnnouncement, error) {
6871
Addresses: testAddrs,
6972
NodeID: priv.PubKey(),
7073
Alias: alias,
74+
Features: testFeatures,
7175
}, nil
7276
}
7377

routing/pathfind_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
167167
Addresses: testAddrs,
168168
PubKey: pub,
169169
Alias: node.Alias,
170+
Features: testFeatures,
170171
}
171172

172173
// We require all aliases within the graph to be unique for our

0 commit comments

Comments
 (0)