From 0fd6004958e6a648a5aa579bc8639955f22109b3 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH 01/23] multi: partition lnwire.ChanUpdateFlag into ChannelFlags and MessageFlags In this commit: * we partition lnwire.ChanUpdateFlag into two (ChanUpdateChanFlags and ChanUpdateMsgFlags), from a uint16 to a pair of uint8's * we rename the ChannelUpdate.Flags to ChannelFlags and add an additional MessageFlags field, which will be used to indicate the presence of the optional field HtlcMaximumMsat within the ChannelUpdate. * we partition ChannelEdgePolicy.Flags into message and channel flags. This change corresponds to the partitioning of the ChannelUpdate's Flags field into MessageFlags and ChannelFlags. Co-authored-by: Johan T. Halseth --- autopilot/graph.go | 6 +++-- channeldb/graph.go | 22 +++++++++++----- channeldb/graph_test.go | 48 ++++++++++++++++++++--------------- discovery/chan_series.go | 6 +++-- discovery/gossiper.go | 26 ++++++++++--------- discovery/gossiper_test.go | 14 +++++----- discovery/utils.go | 6 +++-- fundingmanager.go | 5 ++-- lnwire/channel_update.go | 30 +++++++++++++++------- lnwire/lnwire.go | 26 ++++++++++++++----- lnwire/lnwire_test.go | 3 ++- lnwire/onion_error_test.go | 3 ++- peer.go | 8 +++--- routing/notifications.go | 4 +-- routing/notifications_test.go | 4 +-- routing/pathfind.go | 2 +- routing/pathfind_test.go | 19 ++++++++------ routing/router.go | 24 ++++++++++-------- routing/router_test.go | 18 +++++++------ rpcserver.go | 4 +-- server.go | 11 ++++---- 21 files changed, 176 insertions(+), 113 deletions(-) diff --git a/autopilot/graph.go b/autopilot/graph.go index 15440caf..534f7d81 100644 --- a/autopilot/graph.go +++ b/autopilot/graph.go @@ -229,7 +229,8 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, MinHTLC: 1, FeeBaseMSat: 10, FeeProportionalMillionths: 10000, - Flags: 0, + MessageFlags: 0, + ChannelFlags: 0, } if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { @@ -243,7 +244,8 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, MinHTLC: 1, FeeBaseMSat: 10, FeeProportionalMillionths: 10000, - Flags: 1, + MessageFlags: 0, + ChannelFlags: 1, } if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { return nil, nil, err diff --git a/channeldb/graph.go b/channeldb/graph.go index 849afd9f..a5b2c146 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -1677,7 +1677,7 @@ func updateEdgePolicy(edges, edgeIndex, nodes *bbolt.Bucket, // Depending on the flags value passed above, either the first // or second edge policy is being updated. var fromNode, toNode []byte - if edge.Flags&lnwire.ChanUpdateDirection == 0 { + if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 { fromNode = nodeInfo[:33] toNode = nodeInfo[33:66] } else { @@ -2422,9 +2422,13 @@ type ChannelEdgePolicy struct { // was received. LastUpdate time.Time - // Flags is a bitfield which signals the capabilities of the channel as - // well as the directed edge this update applies to. - Flags lnwire.ChanUpdateFlag + // MessageFlags is a bitfield which indicates the presence of optional + // fields (like max_htlc) in the policy. + MessageFlags lnwire.ChanUpdateMsgFlags + + // ChannelFlags is a bitfield which signals the capabilities of the + // channel as well as the directed edge this update applies to. + ChannelFlags lnwire.ChanUpdateChanFlags // TimeLockDelta is the number of blocks this node will subtract from // the expiry of an incoming HTLC. This value expresses the time buffer @@ -3186,7 +3190,10 @@ func putChanEdgePolicy(edges, nodes *bbolt.Bucket, edge *ChannelEdgePolicy, return err } - if err := binary.Write(&b, byteOrder, edge.Flags); err != nil { + if err := binary.Write(&b, byteOrder, edge.MessageFlags); err != nil { + return err + } + if err := binary.Write(&b, byteOrder, edge.ChannelFlags); err != nil { return err } if err := binary.Write(&b, byteOrder, edge.TimeLockDelta); err != nil { @@ -3363,7 +3370,10 @@ func deserializeChanEdgePolicy(r io.Reader, unix := int64(byteOrder.Uint64(scratch[:])) edge.LastUpdate = time.Unix(unix, 0) - if err := binary.Read(r, byteOrder, &edge.Flags); err != nil { + if err := binary.Read(r, byteOrder, &edge.MessageFlags); err != nil { + return nil, err + } + if err := binary.Read(r, byteOrder, &edge.ChannelFlags); err != nil { return nil, err } if err := binary.Read(r, byteOrder, &edge.TimeLockDelta); err != nil { diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index cbd78721..6b3287ba 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -699,7 +699,8 @@ func TestEdgeInfoUpdates(t *testing.T) { SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(433453, 0), - Flags: 0, + MessageFlags: 0, + ChannelFlags: 0, TimeLockDelta: 99, MinHTLC: 2342135, FeeBaseMSat: 4352345, @@ -712,7 +713,8 @@ func TestEdgeInfoUpdates(t *testing.T) { SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(124234, 0), - Flags: 1, + MessageFlags: 0, + ChannelFlags: 1, TimeLockDelta: 99, MinHTLC: 2342135, FeeBaseMSat: 4352345, @@ -792,6 +794,8 @@ func newEdgePolicy(chanID uint64, op wire.OutPoint, db *DB, return &ChannelEdgePolicy{ ChannelID: chanID, LastUpdate: time.Unix(updateTime, 0), + MessageFlags: 0, + ChannelFlags: 0, TimeLockDelta: uint16(prand.Int63()), MinHTLC: lnwire.MilliSatoshi(prand.Int63()), FeeBaseMSat: lnwire.MilliSatoshi(prand.Int63()), @@ -894,7 +898,7 @@ func TestGraphTraversal(t *testing.T) { // Create and add an edge with random data that points from // node1 -> node2. edge := randEdgePolicy(chanID, op, db) - edge.Flags = 0 + edge.ChannelFlags = 0 edge.Node = secondNode edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { @@ -904,7 +908,7 @@ func TestGraphTraversal(t *testing.T) { // Create another random edge that points from node2 -> node1 // this time. edge = randEdgePolicy(chanID, op, db) - edge.Flags = 1 + edge.ChannelFlags = 1 edge.Node = firstNode edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { @@ -1145,7 +1149,7 @@ func TestGraphPruning(t *testing.T) { // Create and add an edge with random data that points from // node_i -> node_i+1 edge := randEdgePolicy(chanID, op, db) - edge.Flags = 0 + edge.ChannelFlags = 0 edge.Node = graphNodes[i] edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { @@ -1155,7 +1159,7 @@ func TestGraphPruning(t *testing.T) { // Create another random edge that points from node_i+1 -> // node_i this time. edge = randEdgePolicy(chanID, op, db) - edge.Flags = 1 + edge.ChannelFlags = 1 edge.Node = graphNodes[i] edge.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge); err != nil { @@ -1414,7 +1418,7 @@ func TestChanUpdatesInHorizon(t *testing.T) { edge1 := newEdgePolicy( chanID.ToUint64(), op, db, edge1UpdateTime.Unix(), ) - edge1.Flags = 0 + edge1.ChannelFlags = 0 edge1.Node = node2 edge1.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge1); err != nil { @@ -1424,7 +1428,7 @@ func TestChanUpdatesInHorizon(t *testing.T) { edge2 := newEdgePolicy( chanID.ToUint64(), op, db, edge2UpdateTime.Unix(), ) - edge2.Flags = 1 + edge2.ChannelFlags = 1 edge2.Node = node1 edge2.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge2); err != nil { @@ -1915,7 +1919,7 @@ func TestFetchChanInfos(t *testing.T) { edge1 := newEdgePolicy( chanID.ToUint64(), op, db, updateTime.Unix(), ) - edge1.Flags = 0 + edge1.ChannelFlags = 0 edge1.Node = node2 edge1.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge1); err != nil { @@ -1925,7 +1929,7 @@ func TestFetchChanInfos(t *testing.T) { edge2 := newEdgePolicy( chanID.ToUint64(), op, db, updateTime.Unix(), ) - edge2.Flags = 1 + edge2.ChannelFlags = 1 edge2.Node = node1 edge2.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge2); err != nil { @@ -2053,7 +2057,7 @@ func TestIncompleteChannelPolicies(t *testing.T) { edgePolicy := newEdgePolicy( chanID.ToUint64(), op, db, updateTime.Unix(), ) - edgePolicy.Flags = 0 + edgePolicy.ChannelFlags = 0 edgePolicy.Node = node2 edgePolicy.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edgePolicy); err != nil { @@ -2068,7 +2072,7 @@ func TestIncompleteChannelPolicies(t *testing.T) { edgePolicy = newEdgePolicy( chanID.ToUint64(), op, db, updateTime.Unix(), ) - edgePolicy.Flags = 1 + edgePolicy.ChannelFlags = 1 edgePolicy.Node = node1 edgePolicy.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edgePolicy); err != nil { @@ -2125,7 +2129,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) { } edge1 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db) - edge1.Flags = 0 + edge1.ChannelFlags = 0 edge1.Node = node1 edge1.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge1); err != nil { @@ -2133,7 +2137,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) { } edge2 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db) - edge2.Flags = 1 + edge2.ChannelFlags = 1 edge2.Node = node2 edge2.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge2); err != nil { @@ -2190,12 +2194,12 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) { // Now, we'll update the edge policies to ensure the old timestamps are // removed from the update index. - edge1.Flags = 2 + edge1.ChannelFlags = 2 edge1.LastUpdate = time.Now() if err := graph.UpdateEdgePolicy(edge1); err != nil { t.Fatalf("unable to update edge: %v", err) } - edge2.Flags = 3 + edge2.ChannelFlags = 3 edge2.LastUpdate = edge1.LastUpdate.Add(time.Hour) if err := graph.UpdateEdgePolicy(edge2); err != nil { t.Fatalf("unable to update edge: %v", err) @@ -2282,7 +2286,7 @@ func TestPruneGraphNodes(t *testing.T) { // We'll now insert an advertised edge, but it'll only be the edge that // points from the first to the second node. edge1 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db) - edge1.Flags = 0 + edge1.ChannelFlags = 0 edge1.Node = node1 edge1.SigBytes = testSig.Serialize() if err := graph.UpdateEdgePolicy(edge1); err != nil { @@ -2645,9 +2649,13 @@ func compareEdgePolicies(a, b *ChannelEdgePolicy) error { return fmt.Errorf("edge 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.MessageFlags != b.MessageFlags { + return fmt.Errorf("MessageFlags doesn't match: expected %v, "+ + "got %v", a.MessageFlags, b.MessageFlags) + } + if a.ChannelFlags != b.ChannelFlags { + return fmt.Errorf("ChannelFlags doesn't match: expected %v, "+ + "got %v", a.ChannelFlags, b.ChannelFlags) } if a.TimeLockDelta != b.TimeLockDelta { return fmt.Errorf("TimeLockDelta doesn't match: expected %v, "+ diff --git a/discovery/chan_series.go b/discovery/chan_series.go index 25ecbed4..86482beb 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -327,7 +327,8 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, ChainHash: chanInfo.ChainHash, ShortChannelID: shortChanID, Timestamp: uint32(e1.LastUpdate.Unix()), - Flags: e1.Flags, + MessageFlags: e1.MessageFlags, + ChannelFlags: e1.ChannelFlags, TimeLockDelta: e1.TimeLockDelta, HtlcMinimumMsat: e1.MinHTLC, BaseFee: uint32(e1.FeeBaseMSat), @@ -346,7 +347,8 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, ChainHash: chanInfo.ChainHash, ShortChannelID: shortChanID, Timestamp: uint32(e2.LastUpdate.Unix()), - Flags: e2.Flags, + MessageFlags: e2.MessageFlags, + ChannelFlags: e2.ChannelFlags, TimeLockDelta: e2.TimeLockDelta, HtlcMinimumMsat: e2.MinHTLC, BaseFee: uint32(e2.FeeBaseMSat), diff --git a/discovery/gossiper.go b/discovery/gossiper.go index b74a2f65..76afb895 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -560,7 +560,7 @@ func (d *AuthenticatedGossiper) ProcessLocalAnnouncement(msg lnwire.Message, } // channelUpdateID is a unique identifier for ChannelUpdate messages, as -// channel updates can be identified by the (ShortChannelID, Flags) +// channel updates can be identified by the (ShortChannelID, ChannelFlags) // tuple. type channelUpdateID struct { // channelID represents the set of data which is needed to @@ -570,7 +570,7 @@ type channelUpdateID struct { // Flags least-significant bit must be set to 0 if the creating node // corresponds to the first node in the previously sent channel // announcement and 1 otherwise. - flags lnwire.ChanUpdateFlag + flags lnwire.ChanUpdateChanFlags } // msgWithSenders is a wrapper struct around a message, and the set of peers @@ -669,13 +669,13 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) { mws.senders[sender] = struct{}{} d.channelAnnouncements[deDupKey] = mws - // Channel updates are identified by the (short channel id, flags) - // tuple. + // Channel updates are identified by the (short channel id, + // channelflags) tuple. case *lnwire.ChannelUpdate: sender := routing.NewVertex(message.source) deDupKey := channelUpdateID{ msg.ShortChannelID, - msg.Flags, + msg.ChannelFlags, } oldTimestamp := uint32(0) @@ -1911,7 +1911,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // announcement for this edge. timestamp := time.Unix(int64(msg.Timestamp), 0) if d.cfg.Router.IsStaleEdgePolicy( - msg.ShortChannelID, timestamp, msg.Flags, + msg.ShortChannelID, timestamp, msg.ChannelFlags, ) { nMsg.err <- nil @@ -1986,9 +1986,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // edge is being updated. var pubKey *btcec.PublicKey switch { - case msg.Flags&lnwire.ChanUpdateDirection == 0: + case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0: pubKey, _ = chanInfo.NodeKey1() - case msg.Flags&lnwire.ChanUpdateDirection == 1: + case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1: pubKey, _ = chanInfo.NodeKey2() } @@ -2009,7 +2009,8 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( SigBytes: msg.Signature.ToSignatureBytes(), ChannelID: shortChanID, LastUpdate: timestamp, - Flags: msg.Flags, + MessageFlags: msg.MessageFlags, + ChannelFlags: msg.ChannelFlags, TimeLockDelta: msg.TimeLockDelta, MinHTLC: msg.HtlcMinimumMsat, FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), @@ -2041,9 +2042,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // Get our peer's public key. var remotePub *btcec.PublicKey switch { - case msg.Flags&lnwire.ChanUpdateDirection == 0: + case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0: remotePub, _ = chanInfo.NodeKey2() - case msg.Flags&lnwire.ChanUpdateDirection == 1: + case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1: remotePub, _ = chanInfo.NodeKey1() } @@ -2517,7 +2518,8 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID), Timestamp: uint32(timestamp), - Flags: edge.Flags, + MessageFlags: edge.MessageFlags, + ChannelFlags: edge.ChannelFlags, TimeLockDelta: edge.TimeLockDelta, HtlcMinimumMsat: edge.MinHTLC, BaseFee: uint32(edge.FeeBaseMSat), diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index a1db0e70..230e0dd1 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -258,7 +258,7 @@ func (r *mockGraphSource) IsKnownEdge(chanID lnwire.ShortChannelID) bool { // IsStaleEdgePolicy returns true if the graph source has a channel edge for // the passed channel ID (and flags) that have a more recent timestamp. func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID, - timestamp time.Time, flags lnwire.ChanUpdateFlag) bool { + timestamp time.Time, flags lnwire.ChanUpdateChanFlags) bool { edges, ok := r.edges[chanID.ToUint64()] if !ok { @@ -267,10 +267,10 @@ func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID, switch { - case len(edges) >= 1 && edges[0].Flags == flags: + case len(edges) >= 1 && edges[0].ChannelFlags == flags: return !edges[0].LastUpdate.Before(timestamp) - case len(edges) >= 2 && edges[1].Flags == flags: + case len(edges) >= 2 && edges[1].ChannelFlags == flags: return !edges[1].LastUpdate.Before(timestamp) default: @@ -440,7 +440,8 @@ func createNodeAnnouncement(priv *btcec.PrivateKey, return a, nil } -func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, +func createUpdateAnnouncement(blockHeight uint32, + flags lnwire.ChanUpdateChanFlags, nodeKey *btcec.PrivateKey, timestamp uint32, extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) { @@ -451,8 +452,9 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, BlockHeight: blockHeight, }, Timestamp: timestamp, + MessageFlags: 0, + ChannelFlags: flags, TimeLockDelta: uint16(prand.Int63()), - Flags: flags, HtlcMinimumMsat: lnwire.MilliSatoshi(prand.Int63()), FeeRate: uint32(prand.Int31()), BaseFee: uint32(prand.Int31()), @@ -2028,7 +2030,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) { assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) { channelKey := channelUpdateID{ ua3.ShortChannelID, - ua3.Flags, + ua3.ChannelFlags, } mws, ok := announcements.channelUpdates[channelKey] diff --git a/discovery/utils.go b/discovery/utils.go index 63143c86..d214332e 100644 --- a/discovery/utils.go +++ b/discovery/utils.go @@ -72,7 +72,8 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, ChainHash: chanInfo.ChainHash, ShortChannelID: chanID, Timestamp: uint32(e1.LastUpdate.Unix()), - Flags: e1.Flags, + MessageFlags: e1.MessageFlags, + ChannelFlags: e1.ChannelFlags, TimeLockDelta: e1.TimeLockDelta, HtlcMinimumMsat: e1.MinHTLC, BaseFee: uint32(e1.FeeBaseMSat), @@ -89,7 +90,8 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, ChainHash: chanInfo.ChainHash, ShortChannelID: chanID, Timestamp: uint32(e2.LastUpdate.Unix()), - Flags: e2.Flags, + MessageFlags: e2.MessageFlags, + ChannelFlags: e2.ChannelFlags, TimeLockDelta: e2.TimeLockDelta, HtlcMinimumMsat: e2.MinHTLC, BaseFee: uint32(e2.FeeBaseMSat), diff --git a/fundingmanager.go b/fundingmanager.go index a5914f29..f2591206 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -2468,7 +2468,7 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, // being updated within the ChannelUpdateAnnouncement announcement // below. A value of zero means it's the edge of the "first" node and 1 // being the other node. - var chanFlags lnwire.ChanUpdateFlag + var chanFlags lnwire.ChanUpdateChanFlags // The lexicographical ordering of the two identity public keys of the // nodes indicates which of the nodes is "first". If our serialized @@ -2502,7 +2502,8 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, ShortChannelID: shortChanID, ChainHash: chainHash, Timestamp: uint32(time.Now().Unix()), - Flags: chanFlags, + MessageFlags: 0, + ChannelFlags: chanFlags, TimeLockDelta: uint16(f.cfg.DefaultRoutingPolicy.TimeLockDelta), // We use the HtlcMinimumMsat that the remote party required us diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index af7fbd28..530bd3ab 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -8,16 +8,20 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" ) -// ChanUpdateFlag is a bitfield that signals various options concerning a +// ChanUpdateMsgFlags is a bitfield that signals whether optional fields are +// present in the ChannelUpdate. +type ChanUpdateMsgFlags uint8 + +// ChanUpdateChanFlags is a bitfield that signals various options concerning a // particular channel edge. Each bit is to be examined in order to determine // how the ChannelUpdate message is to be interpreted. -type ChanUpdateFlag uint16 +type ChanUpdateChanFlags uint8 const ( // ChanUpdateDirection indicates the direction of a channel update. If // this bit is set to 0 if Node1 (the node with the "smaller" Node ID) // is updating the channel, and to 1 otherwise. - ChanUpdateDirection ChanUpdateFlag = 1 << iota + ChanUpdateDirection ChanUpdateChanFlags = 1 << iota // ChanUpdateDisabled is a bit that indicates if the channel edge // selected by the ChanUpdateDirection bit is to be treated as being @@ -48,13 +52,18 @@ type ChannelUpdate struct { // the last-received. Timestamp uint32 - // Flags is a bitfield that describes additional meta-data concerning - // how the update is to be interpreted. Currently, the + // MessageFlags is a bitfield that describes whether optional fields + // are present in this update. Currently, the least-significant bit + // must be set to 1 if the optional field MaxHtlc is present. + MessageFlags ChanUpdateMsgFlags + + // ChannelFlags is a bitfield that describes additional meta-data + // concerning how the update is to be interpreted. Currently, the // least-significant bit must be set to 0 if the creating node // corresponds to the first node in the previously sent channel // announcement and 1 otherwise. If the second bit is set, then the // channel is set to be disabled. - Flags ChanUpdateFlag + ChannelFlags ChanUpdateChanFlags // TimeLockDelta is the minimum number of blocks this node requires to // be added to the expiry of HTLCs. This is a security parameter @@ -98,7 +107,8 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { a.ChainHash[:], &a.ShortChannelID, &a.Timestamp, - &a.Flags, + &a.MessageFlags, + &a.ChannelFlags, &a.TimeLockDelta, &a.HtlcMinimumMsat, &a.BaseFee, @@ -133,7 +143,8 @@ func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error { a.ChainHash[:], a.ShortChannelID, a.Timestamp, - a.Flags, + a.MessageFlags, + a.ChannelFlags, a.TimeLockDelta, a.HtlcMinimumMsat, a.BaseFee, @@ -168,7 +179,8 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) { a.ChainHash[:], a.ShortChannelID, a.Timestamp, - a.Flags, + a.MessageFlags, + a.ChannelFlags, a.TimeLockDelta, a.HtlcMinimumMsat, a.BaseFee, diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index de0edaf2..e9930497 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -105,9 +105,15 @@ func WriteElement(w io.Writer, element interface{}) error { if _, err := w.Write(b[:]); err != nil { return err } - case ChanUpdateFlag: - var b [2]byte - binary.BigEndian.PutUint16(b[:], uint16(e)) + case ChanUpdateMsgFlags: + var b [1]byte + b[0] = uint8(e) + if _, err := w.Write(b[:]); err != nil { + return err + } + case ChanUpdateChanFlags: + var b [1]byte + b[0] = uint8(e) if _, err := w.Write(b[:]); err != nil { return err } @@ -470,12 +476,18 @@ func ReadElement(r io.Reader, element interface{}) error { return err } *e = binary.BigEndian.Uint16(b[:]) - case *ChanUpdateFlag: - var b [2]byte - if _, err := io.ReadFull(r, b[:]); err != nil { + case *ChanUpdateMsgFlags: + var b [1]uint8 + if _, err := r.Read(b[:]); err != nil { return err } - *e = ChanUpdateFlag(binary.BigEndian.Uint16(b[:])) + *e = ChanUpdateMsgFlags(b[0]) + case *ChanUpdateChanFlags: + var b [1]uint8 + if _, err := r.Read(b[:]); err != nil { + return err + } + *e = ChanUpdateChanFlags(b[0]) case *ErrorCode: var b [2]byte if _, err := io.ReadFull(r, b[:]); err != nil { diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 013295e3..d7c59193 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -608,7 +608,8 @@ func TestLightningWireProtocol(t *testing.T) { req := ChannelUpdate{ ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), Timestamp: uint32(r.Int31()), - Flags: ChanUpdateFlag(r.Int31()), + MessageFlags: ChanUpdateMsgFlags(r.Int31()), + ChannelFlags: ChanUpdateChanFlags(r.Int31()), TimeLockDelta: uint16(r.Int31()), HtlcMinimumMsat: MilliSatoshi(r.Int63()), BaseFee: uint32(r.Int31()), diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go index 62b92766..3cc7d49c 100644 --- a/lnwire/onion_error_test.go +++ b/lnwire/onion_error_test.go @@ -20,7 +20,8 @@ var ( Signature: sig, ShortChannelID: NewShortChanIDFromInt(1), Timestamp: 1, - Flags: 1, + MessageFlags: 0, + ChannelFlags: 1, } ) diff --git a/peer.go b/peer.go index 62a8d297..148088c2 100644 --- a/peer.go +++ b/peer.go @@ -1235,10 +1235,10 @@ func messageSummary(msg lnwire.Message) string { msg.ChainHash, msg.ShortChannelID.ToUint64()) case *lnwire.ChannelUpdate: - return fmt.Sprintf("chain_hash=%v, short_chan_id=%v, flag=%v, "+ - "update_time=%v", msg.ChainHash, - msg.ShortChannelID.ToUint64(), msg.Flags, - time.Unix(int64(msg.Timestamp), 0)) + return fmt.Sprintf("chain_hash=%v, short_chan_id=%v, "+ + "mflags=%v, cflags=%v, update_time=%v", msg.ChainHash, + msg.ShortChannelID.ToUint64(), msg.MessageFlags, + msg.ChannelFlags, time.Unix(int64(msg.Timestamp), 0)) case *lnwire.NodeAnnouncement: return fmt.Sprintf("node=%x, update_time=%v", diff --git a/routing/notifications.go b/routing/notifications.go index 64958228..6ea68d5d 100644 --- a/routing/notifications.go +++ b/routing/notifications.go @@ -339,7 +339,7 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, // the second node. sourceNode := edgeInfo.NodeKey1 connectingNode := edgeInfo.NodeKey2 - if m.Flags&lnwire.ChanUpdateDirection == 1 { + if m.ChannelFlags&lnwire.ChanUpdateDirection == 1 { sourceNode = edgeInfo.NodeKey2 connectingNode = edgeInfo.NodeKey1 } @@ -363,7 +363,7 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, FeeRate: m.FeeProportionalMillionths, AdvertisingNode: aNode, ConnectingNode: cNode, - Disabled: m.Flags&lnwire.ChanUpdateDisabled != 0, + Disabled: m.ChannelFlags&lnwire.ChanUpdateDisabled != 0, } edgeUpdate.AdvertisingNode.Curve = nil edgeUpdate.ConnectingNode.Curve = nil diff --git a/routing/notifications_test.go b/routing/notifications_test.go index 8214050f..7f8aa8fa 100644 --- a/routing/notifications_test.go +++ b/routing/notifications_test.go @@ -402,9 +402,9 @@ func TestEdgeUpdateNotification(t *testing.T) { // Create random policy edges that are stemmed to the channel id // created above. edge1 := randEdgePolicy(chanID, node1) - edge1.Flags = 0 + edge1.ChannelFlags = 0 edge2 := randEdgePolicy(chanID, node2) - edge2.Flags = 1 + edge2.ChannelFlags = 1 if err := ctx.router.UpdateEdge(edge1); err != nil { t.Fatalf("unable to add edge update: %v", err) diff --git a/routing/pathfind.go b/routing/pathfind.go index c3035711..7fcad67d 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -562,7 +562,7 @@ func findPath(g *graphParams, r *restrictParams, // TODO(halseth): also ignore disable flags for non-local // channels if bandwidth hint is set? isSourceChan := fromVertex == sourceVertex - edgeFlags := lnwire.ChanUpdateFlag(edge.Flags) + edgeFlags := edge.ChannelFlags isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0 if !isSourceChan && isDisabled { diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 81278f40..8357522d 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -271,7 +271,8 @@ func parseTestGraph(path string) (*testGraphInstance, error) { edgePolicy := &channeldb.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), - Flags: lnwire.ChanUpdateFlag(edge.Flags), + MessageFlags: lnwire.ChanUpdateMsgFlags(edge.Flags >> 8), + ChannelFlags: lnwire.ChanUpdateChanFlags(edge.Flags), ChannelID: edge.ChannelID, LastUpdate: testTime, TimeLockDelta: edge.Expiry, @@ -487,7 +488,8 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc edgePolicy := &channeldb.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), - Flags: lnwire.ChanUpdateFlag(0), + MessageFlags: 0, + ChannelFlags: 0, ChannelID: channelID, LastUpdate: testTime, TimeLockDelta: testChannel.Node1.Expiry, @@ -501,7 +503,8 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc edgePolicy = &channeldb.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), - Flags: lnwire.ChanUpdateFlag(lnwire.ChanUpdateDirection), + MessageFlags: 0, + ChannelFlags: lnwire.ChanUpdateDirection, ChannelID: channelID, LastUpdate: testTime, TimeLockDelta: testChannel.Node2.Expiry, @@ -1476,11 +1479,11 @@ func TestRouteFailDisabledEdge(t *testing.T) { if err != nil { t.Fatalf("unable to fetch edge: %v", err) } - e1.Flags |= lnwire.ChanUpdateDisabled + e1.ChannelFlags |= lnwire.ChanUpdateDisabled if err := graph.graph.UpdateEdgePolicy(e1); err != nil { t.Fatalf("unable to update edge: %v", err) } - e2.Flags |= lnwire.ChanUpdateDisabled + e2.ChannelFlags |= lnwire.ChanUpdateDisabled if err := graph.graph.UpdateEdgePolicy(e2); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -1507,7 +1510,7 @@ func TestRouteFailDisabledEdge(t *testing.T) { if err != nil { t.Fatalf("unable to fetch edge: %v", err) } - e.Flags |= lnwire.ChanUpdateDisabled + e.ChannelFlags |= lnwire.ChanUpdateDisabled if err := graph.graph.UpdateEdgePolicy(e); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -1627,11 +1630,11 @@ func TestPathSourceEdgesBandwidth(t *testing.T) { if err != nil { t.Fatalf("unable to fetch edge: %v", err) } - e1.Flags |= lnwire.ChanUpdateDisabled + e1.ChannelFlags |= lnwire.ChanUpdateDisabled if err := graph.graph.UpdateEdgePolicy(e1); err != nil { t.Fatalf("unable to update edge: %v", err) } - e2.Flags |= lnwire.ChanUpdateDisabled + e2.ChannelFlags |= lnwire.ChanUpdateDisabled if err := graph.graph.UpdateEdgePolicy(e2); err != nil { t.Fatalf("unable to update edge: %v", err) } diff --git a/routing/router.go b/routing/router.go index adc1efb6..35efb8d6 100644 --- a/routing/router.go +++ b/routing/router.go @@ -84,7 +84,7 @@ type ChannelGraphSource interface { // edge for the passed channel ID (and flags) that have a more recent // timestamp. IsStaleEdgePolicy(chanID lnwire.ShortChannelID, timestamp time.Time, - flags lnwire.ChanUpdateFlag) bool + flags lnwire.ChanUpdateChanFlags) bool // ForAllOutgoingChannels is used to iterate over all channels // emanating from the "source" node which is the center of the @@ -243,7 +243,7 @@ func newEdgeLocatorByPubkeys(channelID uint64, fromNode, toNode *Vertex) *edgeLo func newEdgeLocator(edge *channeldb.ChannelEdgePolicy) *edgeLocator { return &edgeLocator{ channelID: edge.ChannelID, - direction: uint8(edge.Flags & lnwire.ChanUpdateDirection), + direction: uint8(edge.ChannelFlags & lnwire.ChanUpdateDirection), } } @@ -1149,25 +1149,26 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // A flag set of 0 indicates this is an announcement for the // "first" node in the channel. - case msg.Flags&lnwire.ChanUpdateDirection == 0: + case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0: // Ignore outdated message. if !edge1Timestamp.Before(msg.LastUpdate) { return newErrf(ErrOutdated, "Ignoring "+ - "outdated update (flags=%v) for known "+ - "chan_id=%v", msg.Flags, msg.ChannelID) - + "outdated update (flags=%v|%v) for "+ + "known chan_id=%v", msg.MessageFlags, + msg.ChannelFlags, msg.ChannelID) } // Similarly, a flag set of 1 indicates this is an announcement // for the "second" node in the channel. - case msg.Flags&lnwire.ChanUpdateDirection == 1: + case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1: // Ignore outdated message. if !edge2Timestamp.Before(msg.LastUpdate) { return newErrf(ErrOutdated, "Ignoring "+ - "outdated update (flags=%v) for known "+ - "chan_id=%v", msg.Flags, msg.ChannelID) + "outdated update (flags=%v|%v) for "+ + "known chan_id=%v", msg.MessageFlags, + msg.ChannelFlags, msg.ChannelID) } } @@ -2068,7 +2069,8 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate, SigBytes: msg.Signature.ToSignatureBytes(), ChannelID: msg.ShortChannelID.ToUint64(), LastUpdate: time.Unix(int64(msg.Timestamp), 0), - Flags: msg.Flags, + MessageFlags: msg.MessageFlags, + ChannelFlags: msg.ChannelFlags, TimeLockDelta: msg.TimeLockDelta, MinHTLC: msg.HtlcMinimumMsat, FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), @@ -2270,7 +2272,7 @@ func (r *ChannelRouter) IsKnownEdge(chanID lnwire.ShortChannelID) bool { // // NOTE: This method is part of the ChannelGraphSource interface. func (r *ChannelRouter) IsStaleEdgePolicy(chanID lnwire.ShortChannelID, - timestamp time.Time, flags lnwire.ChanUpdateFlag) bool { + timestamp time.Time, flags lnwire.ChanUpdateChanFlags) bool { edge1Timestamp, edge2Timestamp, exists, err := r.cfg.Graph.HasChannelEdge( chanID.ToUint64(), diff --git a/routing/router_test.go b/routing/router_test.go index ac60a549..2ccaf4da 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -550,7 +550,8 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) { errChanUpdate := lnwire.ChannelUpdate{ ShortChannelID: lnwire.NewShortChanIDFromInt(chanID), Timestamp: uint32(edgeUpateToFail.LastUpdate.Unix()), - Flags: edgeUpateToFail.Flags, + MessageFlags: edgeUpateToFail.MessageFlags, + ChannelFlags: edgeUpateToFail.ChannelFlags, TimeLockDelta: edgeUpateToFail.TimeLockDelta, HtlcMinimumMsat: edgeUpateToFail.MinHTLC, BaseFee: uint32(edgeUpateToFail.FeeBaseMSat), @@ -656,7 +657,8 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) { errChanUpdate := lnwire.ChannelUpdate{ ShortChannelID: lnwire.NewShortChanIDFromInt(chanID), Timestamp: uint32(edgeUpateToFail.LastUpdate.Unix()), - Flags: edgeUpateToFail.Flags, + MessageFlags: edgeUpateToFail.MessageFlags, + ChannelFlags: edgeUpateToFail.ChannelFlags, TimeLockDelta: edgeUpateToFail.TimeLockDelta, HtlcMinimumMsat: edgeUpateToFail.MinHTLC, BaseFee: uint32(edgeUpateToFail.FeeBaseMSat), @@ -1098,7 +1100,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { FeeBaseMSat: 10, FeeProportionalMillionths: 10000, } - edgePolicy.Flags = 0 + edgePolicy.ChannelFlags = 0 if err := ctx.router.UpdateEdge(edgePolicy); err != nil { t.Fatalf("unable to update edge policy: %v", err) @@ -1114,7 +1116,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { FeeBaseMSat: 10, FeeProportionalMillionths: 10000, } - edgePolicy.Flags = 1 + edgePolicy.ChannelFlags = 1 if err := ctx.router.UpdateEdge(edgePolicy); err != nil { t.Fatalf("unable to update edge policy: %v", err) @@ -1194,7 +1196,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { FeeBaseMSat: 10, FeeProportionalMillionths: 10000, } - edgePolicy.Flags = 0 + edgePolicy.ChannelFlags = 0 if err := ctx.router.UpdateEdge(edgePolicy); err != nil { t.Fatalf("unable to update edge policy: %v", err) @@ -1209,7 +1211,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) { FeeBaseMSat: 10, FeeProportionalMillionths: 10000, } - edgePolicy.Flags = 1 + edgePolicy.ChannelFlags = 1 if err := ctx.router.UpdateEdge(edgePolicy); err != nil { t.Fatalf("unable to update edge policy: %v", err) @@ -2099,7 +2101,7 @@ func TestIsStaleEdgePolicy(t *testing.T) { FeeBaseMSat: 10, FeeProportionalMillionths: 10000, } - edgePolicy.Flags = 0 + edgePolicy.ChannelFlags = 0 if err := ctx.router.UpdateEdge(edgePolicy); err != nil { t.Fatalf("unable to update edge policy: %v", err) } @@ -2113,7 +2115,7 @@ func TestIsStaleEdgePolicy(t *testing.T) { FeeBaseMSat: 10, FeeProportionalMillionths: 10000, } - edgePolicy.Flags = 1 + edgePolicy.ChannelFlags = 1 if err := ctx.router.UpdateEdge(edgePolicy); err != nil { t.Fatalf("unable to update edge policy: %v", err) } diff --git a/rpcserver.go b/rpcserver.go index 90221885..a9cab8a8 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3769,7 +3769,7 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo, MinHtlc: int64(c1.MinHTLC), FeeBaseMsat: int64(c1.FeeBaseMSat), FeeRateMilliMsat: int64(c1.FeeProportionalMillionths), - Disabled: c1.Flags&lnwire.ChanUpdateDisabled != 0, + Disabled: c1.ChannelFlags&lnwire.ChanUpdateDisabled != 0, } } @@ -3779,7 +3779,7 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo, MinHtlc: int64(c2.MinHTLC), FeeBaseMsat: int64(c2.FeeBaseMSat), FeeRateMilliMsat: int64(c2.FeeProportionalMillionths), - Disabled: c2.Flags&lnwire.ChanUpdateDisabled != 0, + Disabled: c2.ChannelFlags&lnwire.ChanUpdateDisabled != 0, } } diff --git a/server.go b/server.go index 7f423e6e..7ef8a147 100644 --- a/server.go +++ b/server.go @@ -2978,10 +2978,10 @@ func (s *server) announceChanStatus(op wire.OutPoint, disabled bool) error { if disabled { // Set the bit responsible for marking a channel as disabled. - chanUpdate.Flags |= lnwire.ChanUpdateDisabled + chanUpdate.ChannelFlags |= lnwire.ChanUpdateDisabled } else { // Clear the bit responsible for marking a channel as disabled. - chanUpdate.Flags &= ^lnwire.ChanUpdateDisabled + chanUpdate.ChannelFlags &= ^lnwire.ChanUpdateDisabled } // We must now update the message's timestamp and generate a new @@ -3066,9 +3066,9 @@ func extractChannelUpdate(ownerPubKey []byte, owner := func(edge *channeldb.ChannelEdgePolicy) []byte { var pubKey *btcec.PublicKey switch { - case edge.Flags&lnwire.ChanUpdateDirection == 0: + case edge.ChannelFlags&lnwire.ChanUpdateDirection == 0: pubKey, _ = info.NodeKey1() - case edge.Flags&lnwire.ChanUpdateDirection == 1: + case edge.ChannelFlags&lnwire.ChanUpdateDirection == 1: pubKey, _ = info.NodeKey2() } @@ -3100,7 +3100,8 @@ func createChannelUpdate(info *channeldb.ChannelEdgeInfo, ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID), Timestamp: uint32(policy.LastUpdate.Unix()), - Flags: policy.Flags, + MessageFlags: policy.MessageFlags, + ChannelFlags: policy.ChannelFlags, TimeLockDelta: policy.TimeLockDelta, HtlcMinimumMsat: policy.MinHTLC, BaseFee: uint32(policy.FeeBaseMSat), From f0ba4b454c1496c6c0ab4a82c62f48f0ef6c2d1d Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH 02/23] lnwire/channel_update: add String method for ChanUpdate[Chan|Msg]Flags In this commit, we fix the problem where it's annoying to parse a bitfield printed out in decimal by writing a String method for the ChanUpdate[Chan|Msg]Flags bitfield. Co-authored-by: Johan T. Halseth --- lnwire/channel_update.go | 11 ++++++++++ lnwire/lnwire_test.go | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 530bd3ab..02dfa5db 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -2,6 +2,7 @@ package lnwire import ( "bytes" + "fmt" "io" "io/ioutil" @@ -12,6 +13,11 @@ import ( // present in the ChannelUpdate. type ChanUpdateMsgFlags uint8 +// String returns the bitfield flags as a string. +func (c ChanUpdateMsgFlags) String() string { + return fmt.Sprintf("%08b", c) +} + // ChanUpdateChanFlags is a bitfield that signals various options concerning a // particular channel edge. Each bit is to be examined in order to determine // how the ChannelUpdate message is to be interpreted. @@ -29,6 +35,11 @@ const ( ChanUpdateDisabled ) +// String returns the bitfield flags as a string. +func (c ChanUpdateChanFlags) String() string { + return fmt.Sprintf("%08b", c) +} + // ChannelUpdate message is used after channel has been initially announced. // Each side independently announces its fees and minimum expiry for HTLCs and // other parameters. Also this message is used to redeclare initially set diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index d7c59193..91de511b 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -178,6 +178,50 @@ func randAddrs(r *rand.Rand) ([]net.Addr, error) { return []net.Addr{tcp4Addr, tcp6Addr, v2OnionAddr, v3OnionAddr}, nil } +// TestChanUpdateChanFlags ensures that converting the ChanUpdateChanFlags and +// ChanUpdateMsgFlags bitfields to a string behaves as expected. +func TestChanUpdateChanFlags(t *testing.T) { + t.Parallel() + + testCases := []struct { + flags uint8 + expected string + }{ + { + flags: 0, + expected: "00000000", + }, + { + flags: 1, + expected: "00000001", + }, + { + flags: 3, + expected: "00000011", + }, + { + flags: 255, + expected: "11111111", + }, + } + + for _, test := range testCases { + chanFlag := ChanUpdateChanFlags(test.flags) + toStr := chanFlag.String() + if toStr != test.expected { + t.Fatalf("expected %v, got %v", + test.expected, toStr) + } + + msgFlag := ChanUpdateMsgFlags(test.flags) + toStr = msgFlag.String() + if toStr != test.expected { + t.Fatalf("expected %v, got %v", + test.expected, toStr) + } + } +} + func TestMaxOutPointIndex(t *testing.T) { t.Parallel() From b49637fbe99d676086d319cfa42b31cd373cc994 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH 03/23] lnwire: add HtlcMaximumMsat field to ChannelUpdate In this commit, we add a field to the ChannelUpdate denoting the maximum HTLC we support sending over this channel, a field which was recently added to the spec. This field serves multiple purposes. In the short term, it enables nodes to signal the largest HTLC they're willing to carry, allows light clients who don't verify channel existence to have some guidance when routing HTLCs, and finally may allow nodes to preserve a portion of bandwidth at all times. In the long term, this field can be used by implementations of AMP to guide payment splitting, as it becomes apparent to a node the largest possible HTLC one can route over a particular channel. This PR was made possible by the merge of #1825, which enables older nodes to properly retain and verify signatures on updates that include new fields (like this new max HTLC field) that they haven't yet been updated to recognize. In addition, the new ChannelUpdate fields are added to the lnwire fuzzing tests. Co-authored-by: Johan T. Halseth --- lnwire/channel_update.go | 47 +++++++++++++++++++++++++++++++++++++--- lnwire/lnwire_test.go | 15 ++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 02dfa5db..1788cdc7 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -13,6 +13,12 @@ import ( // present in the ChannelUpdate. type ChanUpdateMsgFlags uint8 +const ( + // ChanUpdateOptionMaxHtlc is a bit that indicates whether the + // optional htlc_maximum_msat field is present in this ChannelUpdate. + ChanUpdateOptionMaxHtlc ChanUpdateMsgFlags = 1 << iota +) + // String returns the bitfield flags as a string. func (c ChanUpdateMsgFlags) String() string { return fmt.Sprintf("%08b", c) @@ -95,6 +101,9 @@ type ChannelUpdate struct { // satoshi. FeeRate uint32 + // HtlcMaximumMsat is the maximum HTLC value which will be accepted. + HtlcMaximumMsat MilliSatoshi + // 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 @@ -129,6 +138,13 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { return err } + // Now check whether the max HTLC field is present and read it if so. + if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 { + if err := ReadElements(r, &a.HtlcMaximumMsat); err != nil { + return err + } + } + // Now that we've read out all the fields that we explicitly know of, // we'll collect the remainder into the ExtraOpaqueData field. If there // aren't any bytes, then we'll snip off the slice to avoid carrying @@ -149,7 +165,7 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { // // This is part of the lnwire.Message interface. func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error { - return WriteElements(w, + err := WriteElements(w, a.Signature, a.ChainHash[:], a.ShortChannelID, @@ -160,8 +176,21 @@ func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error { a.HtlcMinimumMsat, a.BaseFee, a.FeeRate, - a.ExtraOpaqueData, ) + if err != nil { + return err + } + + // Now append optional fields if they are set. Currently, the only + // optional field is max HTLC. + if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 { + if err := WriteElements(w, a.HtlcMaximumMsat); err != nil { + return err + } + } + + // Finally, append any extra opaque data. + return WriteElements(w, a.ExtraOpaqueData) } // MsgType returns the integer uniquely identifying this message type on the @@ -196,11 +225,23 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) { a.HtlcMinimumMsat, a.BaseFee, a.FeeRate, - a.ExtraOpaqueData, ) if err != nil { return nil, err } + // Now append optional fields if they are set. Currently, the only + // optional field is max HTLC. + if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 { + if err := WriteElements(&w, a.HtlcMaximumMsat); err != nil { + return nil, err + } + } + + // Finally, append any extra opaque data. + if err := WriteElements(&w, a.ExtraOpaqueData); err != nil { + return nil, err + } + return w.Bytes(), nil } diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 91de511b..3046c2f3 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -649,13 +649,26 @@ func TestLightningWireProtocol(t *testing.T) { }, MsgChannelUpdate: func(v []reflect.Value, r *rand.Rand) { var err error + + msgFlags := ChanUpdateMsgFlags(r.Int31()) + maxHtlc := MilliSatoshi(r.Int63()) + + // We make the max_htlc field zero if it is not flagged + // as being part of the ChannelUpdate, to pass + // serialization tests, as it will be ignored if the bit + // is not set. + if msgFlags&ChanUpdateOptionMaxHtlc == 0 { + maxHtlc = 0 + } + req := ChannelUpdate{ ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), Timestamp: uint32(r.Int31()), - MessageFlags: ChanUpdateMsgFlags(r.Int31()), + MessageFlags: msgFlags, ChannelFlags: ChanUpdateChanFlags(r.Int31()), TimeLockDelta: uint16(r.Int31()), HtlcMinimumMsat: MilliSatoshi(r.Int63()), + HtlcMaximumMsat: maxHtlc, BaseFee: uint32(r.Int31()), FeeRate: uint32(r.Int31()), } From 7ab8900eb67cc4561198bc798a85ddacae9237b8 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH 04/23] discovery/gossiper_test: mock AddEdge: set capacity In this commit, we modify the mockGraphSource's `AddEdge` method to set the capacity of the edge it's adding to be a large capacity. This will enable us to test the validation of each ChannelUpdate's max HTLC, since future validation checks will ensure the specified max HTLC is less than total channel capacity. --- discovery/gossiper_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index 230e0dd1..f8c510af 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -17,6 +17,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/chainntnfs" @@ -54,9 +55,10 @@ var ( nodeKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256()) nodeKeyPub2 = nodeKeyPriv2.PubKey() - trickleDelay = time.Millisecond * 100 - retransmitDelay = time.Hour * 1 - proofMatureDelta uint32 + trickleDelay = time.Millisecond * 100 + retransmitDelay = time.Hour * 1 + proofMatureDelta uint32 + maxBtcFundingAmount = btcutil.Amount(1<<62) - 1 ) // makeTestDB creates a new instance of the ChannelDB for testing purposes. A @@ -130,6 +132,10 @@ func (r *mockGraphSource) AddEdge(info *channeldb.ChannelEdgeInfo) error { if _, ok := r.infos[info.ChannelID]; ok { return errors.New("info already exist") } + + // Usually, the capacity is fetched in the router from the funding txout. + // Since the mockGraphSource can't access the txout, assign a default value. + info.Capacity = maxBtcFundingAmount r.infos[info.ChannelID] = info return nil } From f316cc6c7e0f1cbd2e2d3426039398552efa27f9 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH 05/23] discovery/gossiper_test: set ChannelUpdate max htlc In this commit, we alter the gossiper test's helper method that creates channel updates to include the max htlc field in the ChannelUpdates it creates. Co-authored-by: Johan T. Halseth --- discovery/gossiper_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index f8c510af..fca3c254 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -453,15 +453,20 @@ func createUpdateAnnouncement(blockHeight uint32, var err error + htlcMinMsat := lnwire.MilliSatoshi(prand.Int63()) a := &lnwire.ChannelUpdate{ ShortChannelID: lnwire.ShortChannelID{ BlockHeight: blockHeight, }, Timestamp: timestamp, - MessageFlags: 0, + MessageFlags: lnwire.ChanUpdateOptionMaxHtlc, ChannelFlags: flags, TimeLockDelta: uint16(prand.Int63()), - HtlcMinimumMsat: lnwire.MilliSatoshi(prand.Int63()), + HtlcMinimumMsat: htlcMinMsat, + + // Since the max HTLC must be greater than the min HTLC to pass channel + // update validation, set it to double the min htlc. + HtlcMaximumMsat: 2 * htlcMinMsat, FeeRate: uint32(prand.Int31()), BaseFee: uint32(prand.Int31()), } From 15168c391ef3c3c8849e0368393580e43cefea87 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH 06/23] discovery+routing: validate msg flags and max htlc in ChannelUpdates In this commit, we alter the ValidateChannelUpdateAnn function in ann_validation to validate a remote ChannelUpdate's message flags and max HTLC field. If the message flag is set but the max HTLC field is not set or vice versa, the ChannelUpdate fails validation. Co-authored-by: Johan T. Halseth --- discovery/gossiper.go | 11 ++-- discovery/gossiper_test.go | 113 ++++++++++++++++++++++++++++++++++--- routing/ann_validation.go | 34 ++++++++++- routing/router.go | 10 +++- 4 files changed, 150 insertions(+), 18 deletions(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 76afb895..b5f355dc 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1992,10 +1992,11 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( pubKey, _ = chanInfo.NodeKey2() } - // Validate the channel announcement with the expected public - // key, In the case of an invalid channel , we'll return an - // error to the caller and exit early. - if err := routing.ValidateChannelUpdateAnn(pubKey, msg); err != nil { + // Validate the channel announcement with the expected public key and + // channel capacity. In the case of an invalid channel update, we'll + // return an error to the caller and exit early. + err = routing.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, msg) + if err != nil { rErr := fmt.Errorf("unable to validate channel "+ "update announcement for short_chan_id=%v: %v", spew.Sdump(msg.ShortChannelID), err) @@ -2548,7 +2549,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, // To ensure that our signature is valid, we'll verify it ourself // before committing it to the slice returned. - err = routing.ValidateChannelUpdateAnn(d.selfKey, chanUpdate) + err = routing.ValidateChannelUpdateAnn(d.selfKey, info.Capacity, chanUpdate) if err != nil { return nil, nil, fmt.Errorf("generated invalid channel "+ "update sig: %v", err) diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index fca3c254..be029878 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -10,6 +10,7 @@ import ( "net" "os" "reflect" + "strings" "sync" "testing" "time" @@ -474,14 +475,7 @@ func createUpdateAnnouncement(blockHeight uint32, a.ExtraOpaqueData = extraBytes[0] } - pub := nodeKey.PubKey() - signer := mockSigner{nodeKey} - sig, err := SignAnnouncement(&signer, pub, a) - if err != nil { - return nil, err - } - - a.Signature, err = lnwire.NewSigFromSignature(sig) + err = signUpdate(nodeKey, a) if err != nil { return nil, err } @@ -489,6 +483,22 @@ func createUpdateAnnouncement(blockHeight uint32, return a, nil } +func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error { + pub := nodeKey.PubKey() + signer := mockSigner{nodeKey} + sig, err := SignAnnouncement(&signer, pub, a) + if err != nil { + return err + } + + a.Signature, err = lnwire.NewSigFromSignature(sig) + if err != nil { + return err + } + + return nil +} + func createAnnouncementWithoutProof(blockHeight uint32, extraBytes ...[]byte) *lnwire.ChannelAnnouncement { @@ -2765,6 +2775,93 @@ func TestNodeAnnouncementNoChannels(t *testing.T) { } } +// TestOptionalFieldsChannelUpdateValidation tests that we're able to properly +// validate the msg flags and optional max HTLC field of a ChannelUpdate. +func TestOptionalFieldsChannelUpdateValidation(t *testing.T) { + t.Parallel() + + ctx, cleanup, err := createTestCtx(0) + if err != nil { + t.Fatalf("can't create context: %v", err) + } + defer cleanup() + + chanUpdateHeight := uint32(0) + timestamp := uint32(123456) + nodePeer := &mockPeer{nodeKeyPriv1.PubKey(), nil, nil} + + // In this scenario, we'll test whether the message flags field in a channel + // update is properly handled. + chanAnn, err := createRemoteChannelAnnouncement(chanUpdateHeight) + if err != nil { + t.Fatalf("can't create channel announcement: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanAnn, nodePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process announcement: %v", err) + } + + // The first update should fail from an invalid max HTLC field, which is + // less than the min HTLC. + chanUpdAnn, err := createUpdateAnnouncement(0, 0, nodeKeyPriv1, timestamp) + if err != nil { + t.Fatalf("unable to create channel update: %v", err) + } + + chanUpdAnn.HtlcMinimumMsat = 5000 + chanUpdAnn.HtlcMaximumMsat = 4000 + if err := signUpdate(nodeKeyPriv1, chanUpdAnn); err != nil { + t.Fatalf("unable to sign channel update: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn, nodePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err == nil || !strings.Contains(err.Error(), "invalid max htlc") { + t.Fatalf("expected chan update to error, instead got %v", err) + } + + // The second update should fail because the message flag is set but + // the max HTLC field is 0. + chanUpdAnn.HtlcMinimumMsat = 0 + chanUpdAnn.HtlcMaximumMsat = 0 + if err := signUpdate(nodeKeyPriv1, chanUpdAnn); err != nil { + t.Fatalf("unable to sign channel update: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn, nodePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err == nil || !strings.Contains(err.Error(), "invalid max htlc") { + t.Fatalf("expected chan update to error, instead got %v", err) + } + + // The final update should succeed, since setting the flag 0 means the + // nonsense max_htlc field will just be ignored. + chanUpdAnn.MessageFlags = 0 + if err := signUpdate(nodeKeyPriv1, chanUpdAnn); err != nil { + t.Fatalf("unable to sign channel update: %v", err) + } + + select { + case err = <-ctx.gossiper.ProcessRemoteAnnouncement(chanUpdAnn, nodePeer): + case <-time.After(2 * time.Second): + t.Fatal("did not process remote announcement") + } + if err != nil { + t.Fatalf("unable to process announcement: %v", err) + } +} + // mockPeer implements the lnpeer.Peer interface and is used to test the // gossiper's interaction with peers. type mockPeer struct { diff --git a/routing/ann_validation.go b/routing/ann_validation.go index 257ac3fd..5c70dd86 100644 --- a/routing/ann_validation.go +++ b/routing/ann_validation.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcutil" "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/lnwire" @@ -121,11 +122,16 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error { } // ValidateChannelUpdateAnn validates the channel update announcement by -// checking that the included signature covers he announcement and has been -// signed by the node's private key. -func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, +// checking (1) that the included signature covers the announcement and has been +// signed by the node's private key, and (2) that the announcement's message +// flags and optional fields are sane. +func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, a *lnwire.ChannelUpdate) error { + if err := validateOptionalFields(capacity, a); err != nil { + return err + } + data, err := a.DataToSign() if err != nil { return errors.Errorf("unable to reconstruct message: %v", err) @@ -144,3 +150,25 @@ func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, return nil } + +// validateOptionalFields validates a channel update's message flags and +// corresponding update fields. +func validateOptionalFields(capacity btcutil.Amount, + msg *lnwire.ChannelUpdate) error { + + if msg.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc != 0 { + maxHtlc := msg.HtlcMaximumMsat + if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat { + return errors.Errorf("invalid max htlc for channel "+ + "update %v", spew.Sdump(msg)) + } + cap := lnwire.NewMSatFromSatoshis(capacity) + if maxHtlc > cap { + return errors.Errorf("max_htlc(%v) for channel "+ + "update greater than capacity(%v)", maxHtlc, + cap) + } + } + + return nil +} diff --git a/routing/router.go b/routing/router.go index 35efb8d6..49038842 100644 --- a/routing/router.go +++ b/routing/router.go @@ -2060,12 +2060,18 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate, return true } - if err := ValidateChannelUpdateAnn(pubKey, msg); err != nil { + ch, _, _, err := r.GetChannelByID(msg.ShortChannelID) + if err != nil { + log.Errorf("Unable to retrieve channel by id: %v", err) + return false + } + + if err := ValidateChannelUpdateAnn(pubKey, ch.Capacity, msg); err != nil { log.Errorf("Unable to validate channel update: %v", err) return false } - err := r.UpdateEdge(&channeldb.ChannelEdgePolicy{ + err = r.UpdateEdge(&channeldb.ChannelEdgePolicy{ SigBytes: msg.Signature.ToSignatureBytes(), ChannelID: msg.ShortChannelID.ToUint64(), LastUpdate: time.Unix(int64(msg.Timestamp), 0), From 91c9e450313639e0f74bee7211d88645ae54285c Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 07/23] channeldb/graph: check correct bucket for nilness --- channeldb/graph.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channeldb/graph.go b/channeldb/graph.go index a5b2c146..312965be 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -1640,7 +1640,7 @@ func delChannelByEdge(edges *bbolt.Bucket, edgeIndex *bbolt.Bucket, func (c *ChannelGraph) UpdateEdgePolicy(edge *ChannelEdgePolicy) error { return c.db.Update(func(tx *bbolt.Tx) error { edges := tx.Bucket(edgeBucket) - if edge == nil { + if edges == nil { return ErrEdgeNotFound } From b9c5248915d2f06a5d84cc6fbd52d54f416c0156 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 08/23] channeldb/graph: extract ChannelEdgePolicy serialization --- channeldb/graph.go | 100 +++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/channeldb/graph.go b/channeldb/graph.go index 312965be..947561a1 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -3173,57 +3173,15 @@ func putChanEdgePolicy(edges, nodes *bbolt.Bucket, edge *ChannelEdgePolicy, byteOrder.PutUint64(edgeKey[33:], edge.ChannelID) var b bytes.Buffer - - err := wire.WriteVarBytes(&b, 0, edge.SigBytes) - if err != nil { - return err - } - - if err := binary.Write(&b, byteOrder, edge.ChannelID); err != nil { - return err - } - - var scratch [8]byte - updateUnix := uint64(edge.LastUpdate.Unix()) - byteOrder.PutUint64(scratch[:], updateUnix) - if _, err := b.Write(scratch[:]); err != nil { - return err - } - - if err := binary.Write(&b, byteOrder, edge.MessageFlags); err != nil { - return err - } - if err := binary.Write(&b, byteOrder, edge.ChannelFlags); err != nil { - return err - } - if err := binary.Write(&b, byteOrder, edge.TimeLockDelta); err != nil { - return err - } - if err := binary.Write(&b, byteOrder, uint64(edge.MinHTLC)); err != nil { - return err - } - if err := binary.Write(&b, byteOrder, uint64(edge.FeeBaseMSat)); err != nil { - return err - } - if err := binary.Write(&b, byteOrder, uint64(edge.FeeProportionalMillionths)); err != nil { - return err - } - - if _, err := b.Write(to); err != nil { - return err - } - - if len(edge.ExtraOpaqueData) > MaxAllowedExtraOpaqueBytes { - return ErrTooManyExtraOpaqueBytes(len(edge.ExtraOpaqueData)) - } - if err := wire.WriteVarBytes(&b, 0, edge.ExtraOpaqueData); err != nil { + if err := serializeChanEdgePolicy(&b, edge, to); err != nil { return err } // Before we write out the new edge, we'll create a new entry in the // update index in order to keep it fresh. + updateUnix := uint64(edge.LastUpdate.Unix()) var indexKey [8 + 8]byte - copy(indexKey[:], scratch[:]) + byteOrder.PutUint64(indexKey[:8], updateUnix) byteOrder.PutUint64(indexKey[8:], edge.ChannelID) updateIndex, err := edges.CreateBucketIfNotExists(edgeUpdateIndexBucket) @@ -3348,6 +3306,58 @@ func fetchChanEdgePolicies(edgeIndex *bbolt.Bucket, edges *bbolt.Bucket, return edge1, edge2, nil } +func serializeChanEdgePolicy(w io.Writer, edge *ChannelEdgePolicy, + to []byte) error { + + err := wire.WriteVarBytes(w, 0, edge.SigBytes) + if err != nil { + return err + } + + if err := binary.Write(w, byteOrder, edge.ChannelID); err != nil { + return err + } + + var scratch [8]byte + updateUnix := uint64(edge.LastUpdate.Unix()) + byteOrder.PutUint64(scratch[:], updateUnix) + if _, err := w.Write(scratch[:]); err != nil { + return err + } + + if err := binary.Write(w, byteOrder, edge.MessageFlags); err != nil { + return err + } + if err := binary.Write(w, byteOrder, edge.ChannelFlags); err != nil { + return err + } + if err := binary.Write(w, byteOrder, edge.TimeLockDelta); err != nil { + return err + } + if err := binary.Write(w, byteOrder, uint64(edge.MinHTLC)); err != nil { + return err + } + if err := binary.Write(w, byteOrder, uint64(edge.FeeBaseMSat)); err != nil { + return err + } + if err := binary.Write(w, byteOrder, uint64(edge.FeeProportionalMillionths)); err != nil { + return err + } + + if _, err := w.Write(to); err != nil { + return err + } + + if len(edge.ExtraOpaqueData) > MaxAllowedExtraOpaqueBytes { + return ErrTooManyExtraOpaqueBytes(len(edge.ExtraOpaqueData)) + } + + if err := wire.WriteVarBytes(w, 0, edge.ExtraOpaqueData); err != nil { + return err + } + return nil +} + func deserializeChanEdgePolicy(r io.Reader, nodes *bbolt.Bucket) (*ChannelEdgePolicy, error) { From 69d4bf051f28427b144aa9e0deead486393bc987 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 09/23] channeldb/graph: add max HTLC to ChannelEdgePolicy Adding this field will allow us to persist an edge's max HTLC to disk, thus preserving it between restarts. Co-authored-by: Johan T. Halseth --- channeldb/error.go | 6 ++++++ channeldb/graph.go | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/channeldb/error.go b/channeldb/error.go index 15cfa840..c9ddfb49 100644 --- a/channeldb/error.go +++ b/channeldb/error.go @@ -97,6 +97,12 @@ var ( // ErrNoForwardingEvents is returned in the case that a query fails due // to the log not having any recorded events. ErrNoForwardingEvents = fmt.Errorf("no recorded forwarding events") + + // ErrEdgePolicyOptionalFieldNotFound is an error returned if a channel + // policy field is not found in the db even though its message flags + // indicate it should be. + ErrEdgePolicyOptionalFieldNotFound = fmt.Errorf("optional field not " + + "present") ) // ErrTooManyExtraOpaqueBytes creates an error which should be returned if the diff --git a/channeldb/graph.go b/channeldb/graph.go index 947561a1..085d46e7 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -2439,6 +2439,10 @@ type ChannelEdgePolicy struct { // in millisatoshi. MinHTLC lnwire.MilliSatoshi + // MaxHTLC is the largest value HTLC this node will accept, expressed + // in millisatoshi. + MaxHTLC lnwire.MilliSatoshi + // FeeBaseMSat is the base HTLC fee that will be charged for forwarding // ANY HTLC, expressed in mSAT's. FeeBaseMSat lnwire.MilliSatoshi @@ -3348,11 +3352,26 @@ func serializeChanEdgePolicy(w io.Writer, edge *ChannelEdgePolicy, return err } + // If the max_htlc field is present, we write it. To be compatible with + // older versions that wasn't aware of this field, we write it as part + // of the opaque data. + // TODO(halseth): clean up when moving to TLV. + var opaqueBuf bytes.Buffer + if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc != 0 { + err := binary.Write(&opaqueBuf, byteOrder, uint64(edge.MaxHTLC)) + if err != nil { + return err + } + } + if len(edge.ExtraOpaqueData) > MaxAllowedExtraOpaqueBytes { return ErrTooManyExtraOpaqueBytes(len(edge.ExtraOpaqueData)) } + if _, err := opaqueBuf.Write(edge.ExtraOpaqueData); err != nil { + return err + } - if err := wire.WriteVarBytes(w, 0, edge.ExtraOpaqueData); err != nil { + if err := wire.WriteVarBytes(w, 0, opaqueBuf.Bytes()); err != nil { return err } return nil @@ -3416,6 +3435,7 @@ func deserializeChanEdgePolicy(r io.Reader, return nil, fmt.Errorf("unable to fetch node: %x, %v", pub[:], err) } + edge.Node = &node // We'll try and see if there are any opaque bytes left, if not, then // we'll ignore the EOF error and return the edge as is. @@ -3429,6 +3449,25 @@ func deserializeChanEdgePolicy(r io.Reader, return nil, err } - edge.Node = &node + // See if optional fields are present. + if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc != 0 { + // The max_htlc field should be at the beginning of the opaque + // bytes. + opq := edge.ExtraOpaqueData + + // If the max_htlc field is not present, it might be old data + // stored before this field was validated. We'll return the + // edge along with an error. + if len(opq) < 8 { + return edge, ErrEdgePolicyOptionalFieldNotFound + } + + maxHtlc := byteOrder.Uint64(opq[:8]) + edge.MaxHTLC = lnwire.MilliSatoshi(maxHtlc) + + // Exclude the parsed field from the rest of the opaque data. + edge.ExtraOpaqueData = opq[8:] + } + return edge, nil } From 8dd074ee57f6570e371787260b2a9624d7c594a1 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 10/23] channeldb/graph_test: assert MaxHTLC field correctness Co-authored-by: Valentine Wallace --- channeldb/graph_test.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index 6b3287ba..16e50b67 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -699,10 +699,11 @@ func TestEdgeInfoUpdates(t *testing.T) { SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(433453, 0), - MessageFlags: 0, + MessageFlags: 1, ChannelFlags: 0, TimeLockDelta: 99, MinHTLC: 2342135, + MaxHTLC: 13928598, FeeBaseMSat: 4352345, FeeProportionalMillionths: 3452352, Node: secondNode, @@ -713,10 +714,11 @@ func TestEdgeInfoUpdates(t *testing.T) { SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(124234, 0), - MessageFlags: 0, + MessageFlags: 1, ChannelFlags: 1, TimeLockDelta: 99, MinHTLC: 2342135, + MaxHTLC: 13928598, FeeBaseMSat: 4352345, FeeProportionalMillionths: 90392423, Node: firstNode, @@ -794,10 +796,11 @@ func newEdgePolicy(chanID uint64, op wire.OutPoint, db *DB, return &ChannelEdgePolicy{ ChannelID: chanID, LastUpdate: time.Unix(updateTime, 0), - MessageFlags: 0, + MessageFlags: 1, ChannelFlags: 0, TimeLockDelta: uint16(prand.Int63()), MinHTLC: lnwire.MilliSatoshi(prand.Int63()), + MaxHTLC: lnwire.MilliSatoshi(prand.Int63()), FeeBaseMSat: lnwire.MilliSatoshi(prand.Int63()), FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int63()), db: db, @@ -2665,6 +2668,10 @@ func compareEdgePolicies(a, b *ChannelEdgePolicy) error { return fmt.Errorf("MinHTLC doesn't match: expected %v, "+ "got %v", a.MinHTLC, b.MinHTLC) } + if a.MaxHTLC != b.MaxHTLC { + return fmt.Errorf("MaxHTLC doesn't match: expected %v, "+ + "got %v", a.MaxHTLC, b.MaxHTLC) + } if a.FeeBaseMSat != b.FeeBaseMSat { return fmt.Errorf("FeeBaseMSat doesn't match: expected %v, "+ "got %v", a.FeeBaseMSat, b.FeeBaseMSat) From 6fcc76fc68fb4ac7f5476ce6ddbe622ac1fca899 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 11/23] channeldb/channel: ignore ChannelEdgePolicy with missing max_htlc If the max_htlc field is not found when fetching a ChannelEdgePolicy from the DB, we treat this as an unknown policy. This is done to ensure we won't propagate invalid data further. The data will be overwritten with a valid one when we receive an update for this channel. It shouldn't be very common, but old data could be lingering in the DB added before this field was validated. --- channeldb/graph.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/channeldb/graph.go b/channeldb/graph.go index 085d46e7..19a19439 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -3204,11 +3204,15 @@ func putChanEdgePolicy(edges, nodes *bbolt.Bucket, edge *ChannelEdgePolicy, // *prior* update time in order to delete it. To do this, we'll // need to deserialize the existing policy within the database // (now outdated by the new one), and delete its corresponding - // entry within the update index. + // entry within the update index. We'll ignore any + // ErrEdgePolicyOptionalFieldNotFound error, as we only need + // the channel ID and update time to delete the entry. + // TODO(halseth): get rid of these invalid policies in a + // migration. oldEdgePolicy, err := deserializeChanEdgePolicy( bytes.NewReader(edgeBytes), nodes, ) - if err != nil { + if err != nil && err != ErrEdgePolicyOptionalFieldNotFound { return err } @@ -3266,7 +3270,18 @@ func fetchChanEdgePolicy(edges *bbolt.Bucket, chanID []byte, edgeReader := bytes.NewReader(edgeBytes) - return deserializeChanEdgePolicy(edgeReader, nodes) + ep, err := deserializeChanEdgePolicy(edgeReader, nodes) + switch { + // If the db policy was missing an expected optional field, we return + // nil as if the policy was unknown. + case err == ErrEdgePolicyOptionalFieldNotFound: + return nil, nil + + case err != nil: + return nil, err + } + + return ep, nil } func fetchChanEdgePolicies(edgeIndex *bbolt.Bucket, edges *bbolt.Bucket, From 01e679786dbaaa10fbfd05d58f9a8fb9b75bc157 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 12/23] channeldb/graph_test: add TestEdgePolicyMissingMaxHtcl --- channeldb/graph_test.go | 245 ++++++++++++++++++++++++++++++++++------ 1 file changed, 211 insertions(+), 34 deletions(-) diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index 16e50b67..27ed7ba5 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -622,33 +622,8 @@ func assertEdgeInfoEqual(t *testing.T, e1 *ChannelEdgeInfo, } } -func TestEdgeInfoUpdates(t *testing.T) { - t.Parallel() - - db, cleanUp, err := makeTestDB() - defer cleanUp() - if err != nil { - t.Fatalf("unable to make test database: %v", err) - } - - graph := db.ChannelGraph() - - // We'd like to test the update of edges inserted into the database, so - // we create two vertexes to connect. - node1, err := createTestVertex(db) - if err != nil { - t.Fatalf("unable to create test node: %v", err) - } - if err := graph.AddLightningNode(node1); err != nil { - t.Fatalf("unable to add node: %v", err) - } - node2, err := createTestVertex(db) - if err != nil { - t.Fatalf("unable to create test node: %v", err) - } - if err := graph.AddLightningNode(node2); err != nil { - t.Fatalf("unable to add node: %v", err) - } +func createChannelEdge(db *DB, node1, node2 *LightningNode) (*ChannelEdgeInfo, + *ChannelEdgePolicy, *ChannelEdgePolicy) { var ( firstNode *LightningNode @@ -689,12 +664,7 @@ func TestEdgeInfoUpdates(t *testing.T) { copy(edgeInfo.NodeKey2Bytes[:], secondNode.PubKeyBytes[:]) copy(edgeInfo.BitcoinKey1Bytes[:], firstNode.PubKeyBytes[:]) copy(edgeInfo.BitcoinKey2Bytes[:], secondNode.PubKeyBytes[:]) - if err := graph.AddChannelEdge(edgeInfo); err != nil { - t.Fatalf("unable to create channel edge: %v", err) - } - // With the edge added, we can now create some fake edge information to - // update for both edges. edge1 := &ChannelEdgePolicy{ SigBytes: testSig.Serialize(), ChannelID: chanID, @@ -726,8 +696,48 @@ func TestEdgeInfoUpdates(t *testing.T) { db: db, } - // Next, insert both nodes into the database, they should both be - // inserted without any issues. + return edgeInfo, edge1, edge2 +} + +func TestEdgeInfoUpdates(t *testing.T) { + t.Parallel() + + db, cleanUp, err := makeTestDB() + defer cleanUp() + if err != nil { + t.Fatalf("unable to make test database: %v", err) + } + + graph := db.ChannelGraph() + + // We'd like to test the update of edges inserted into the database, so + // we create two vertexes to connect. + node1, err := createTestVertex(db) + if err != nil { + t.Fatalf("unable to create test node: %v", err) + } + if err := graph.AddLightningNode(node1); err != nil { + t.Fatalf("unable to add node: %v", err) + } + node2, err := createTestVertex(db) + if err != nil { + t.Fatalf("unable to create test node: %v", err) + } + if err := graph.AddLightningNode(node2); err != nil { + t.Fatalf("unable to add node: %v", err) + } + + // Create an edge and add it to the db. + edgeInfo, edge1, edge2 := createChannelEdge(db, node1, node2) + if err := graph.AddChannelEdge(edgeInfo); err != nil { + t.Fatalf("unable to create channel edge: %v", err) + } + + chanID := edgeInfo.ChannelID + outpoint := edgeInfo.ChannelPoint + + // Next, insert both edge policies into the database, they should both + // be inserted without any issues. if err := graph.UpdateEdgePolicy(edge1); err != nil { t.Fatalf("unable to update edge: %v", err) } @@ -2601,6 +2611,173 @@ func TestNodeIsPublic(t *testing.T) { ) } +// TestEdgePolicyMissingMaxHtcl tests that if we find a ChannelEdgePolicy in +// the DB that indicates that it should support the htlc_maximum_value_msat +// field, but it is not part of the opaque data, then we'll handle it as it is +// unknown. It also checks that we are correctly able to overwrite it when we +// receive the proper update. +func TestEdgePolicyMissingMaxHtcl(t *testing.T) { + t.Parallel() + + db, cleanUp, err := makeTestDB() + defer cleanUp() + if err != nil { + t.Fatalf("unable to make test database: %v", err) + } + + graph := db.ChannelGraph() + + // We'd like to test the update of edges inserted into the database, so + // we create two vertexes to connect. + node1, err := createTestVertex(db) + if err != nil { + t.Fatalf("unable to create test node: %v", err) + } + if err := graph.AddLightningNode(node1); err != nil { + t.Fatalf("unable to add node: %v", err) + } + node2, err := createTestVertex(db) + if err != nil { + t.Fatalf("unable to create test node: %v", err) + } + + edgeInfo, edge1, edge2 := createChannelEdge(db, node1, node2) + if err := graph.AddLightningNode(node2); err != nil { + t.Fatalf("unable to add node: %v", err) + } + if err := graph.AddChannelEdge(edgeInfo); err != nil { + t.Fatalf("unable to create channel edge: %v", err) + } + + chanID := edgeInfo.ChannelID + from := edge2.Node.PubKeyBytes[:] + to := edge1.Node.PubKeyBytes[:] + + // We'll remove the no max_htlc field from the first edge policy, and + // all other opaque data, and serialize it. + edge1.MessageFlags = 0 + edge1.ExtraOpaqueData = nil + + var b bytes.Buffer + err = serializeChanEdgePolicy(&b, edge1, to) + if err != nil { + t.Fatalf("unable to serialize policy") + } + + // Set the max_htlc field. The extra bytes added to the serialization + // will be the opaque data containing the serialized field. + edge1.MessageFlags = lnwire.ChanUpdateOptionMaxHtlc + edge1.MaxHTLC = 13928598 + var b2 bytes.Buffer + err = serializeChanEdgePolicy(&b2, edge1, to) + if err != nil { + t.Fatalf("unable to serialize policy") + } + + withMaxHtlc := b2.Bytes() + + // Remove the opaque data from the serialization. + stripped := withMaxHtlc[:len(b.Bytes())] + + // Attempting to deserialize these bytes should return an error. + r := bytes.NewReader(stripped) + err = db.View(func(tx *bbolt.Tx) error { + nodes := tx.Bucket(nodeBucket) + if nodes == nil { + return ErrGraphNotFound + } + + _, err = deserializeChanEdgePolicy(r, nodes) + if err != ErrEdgePolicyOptionalFieldNotFound { + t.Fatalf("expected "+ + "ErrEdgePolicyOptionalFieldNotFound, got %v", + err) + } + + return nil + }) + if err != nil { + t.Fatalf("error reading db: %v", err) + } + + // Put the stripped bytes in the DB. + err = db.Update(func(tx *bbolt.Tx) error { + edges := tx.Bucket(edgeBucket) + if edges == nil { + return ErrEdgeNotFound + } + + edgeIndex := edges.Bucket(edgeIndexBucket) + if edgeIndex == nil { + return ErrEdgeNotFound + } + + var edgeKey [33 + 8]byte + copy(edgeKey[:], from) + byteOrder.PutUint64(edgeKey[33:], edge1.ChannelID) + + var scratch [8]byte + var indexKey [8 + 8]byte + copy(indexKey[:], scratch[:]) + byteOrder.PutUint64(indexKey[8:], edge1.ChannelID) + + updateIndex, err := edges.CreateBucketIfNotExists(edgeUpdateIndexBucket) + if err != nil { + return err + } + + if err := updateIndex.Put(indexKey[:], nil); err != nil { + return err + } + + return edges.Put(edgeKey[:], stripped) + }) + if err != nil { + t.Fatalf("error writing db: %v", err) + } + + // And add the second, unmodified edge. + if err := graph.UpdateEdgePolicy(edge2); err != nil { + t.Fatalf("unable to update edge: %v", err) + } + + // Attempt to fetch the edge and policies from the DB. Since the policy + // we added is invalid according to the new format, it should be as we + // are not aware of the policy (indicated by the policy returned being + // nil) + dbEdgeInfo, dbEdge1, dbEdge2, err := graph.FetchChannelEdgesByID(chanID) + if err != nil { + t.Fatalf("unable to fetch channel by ID: %v", err) + } + + // The first edge should have a nil-policy returned + if dbEdge1 != nil { + t.Fatalf("expected db edge to be nil") + } + if err := compareEdgePolicies(dbEdge2, edge2); err != nil { + t.Fatalf("edge doesn't match: %v", err) + } + assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo) + + // Now add the original, unmodified edge policy, and make sure the edge + // policies then become fully populated. + if err := graph.UpdateEdgePolicy(edge1); err != nil { + t.Fatalf("unable to update edge: %v", err) + } + + dbEdgeInfo, dbEdge1, dbEdge2, err = graph.FetchChannelEdgesByID(chanID) + if err != nil { + t.Fatalf("unable to fetch channel by ID: %v", err) + } + if err := compareEdgePolicies(dbEdge1, edge1); err != nil { + t.Fatalf("edge doesn't match: %v", err) + } + if err := compareEdgePolicies(dbEdge2, edge2); err != nil { + t.Fatalf("edge doesn't match: %v", err) + } + assertEdgeInfoEqual(t, dbEdgeInfo, edgeInfo) +} + // 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. From 513ac23479340c3f054ce54ec78519adb44b0acb Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:44 +0100 Subject: [PATCH 13/23] discovery/gossiper: persist remote channel policy updates' max htlc --- discovery/gossiper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index b5f355dc..1413f0bb 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -2014,6 +2014,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( ChannelFlags: msg.ChannelFlags, TimeLockDelta: msg.TimeLockDelta, MinHTLC: msg.HtlcMinimumMsat, + MaxHTLC: msg.HtlcMaximumMsat, FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), ExtraOpaqueData: msg.ExtraOpaqueData, From 19c403711e6d68e79c01dd1fbb60f86cc42fdbe5 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 14/23] discovery/chan_series+utils: include max htlc when syncing with peers In this commit, we ensure that max HTLC is included when we're synchronizing ChannelUpdates with remote peers. --- discovery/chan_series.go | 2 ++ discovery/utils.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/discovery/chan_series.go b/discovery/chan_series.go index 86482beb..b7b77883 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -331,6 +331,7 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, ChannelFlags: e1.ChannelFlags, TimeLockDelta: e1.TimeLockDelta, HtlcMinimumMsat: e1.MinHTLC, + HtlcMaximumMsat: e1.MaxHTLC, BaseFee: uint32(e1.FeeBaseMSat), FeeRate: uint32(e1.FeeProportionalMillionths), ExtraOpaqueData: e1.ExtraOpaqueData, @@ -351,6 +352,7 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, ChannelFlags: e2.ChannelFlags, TimeLockDelta: e2.TimeLockDelta, HtlcMinimumMsat: e2.MinHTLC, + HtlcMaximumMsat: e2.MaxHTLC, BaseFee: uint32(e2.FeeBaseMSat), FeeRate: uint32(e2.FeeProportionalMillionths), ExtraOpaqueData: e2.ExtraOpaqueData, diff --git a/discovery/utils.go b/discovery/utils.go index d214332e..3c0fdc17 100644 --- a/discovery/utils.go +++ b/discovery/utils.go @@ -76,6 +76,7 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, ChannelFlags: e1.ChannelFlags, TimeLockDelta: e1.TimeLockDelta, HtlcMinimumMsat: e1.MinHTLC, + HtlcMaximumMsat: e1.MaxHTLC, BaseFee: uint32(e1.FeeBaseMSat), FeeRate: uint32(e1.FeeProportionalMillionths), ExtraOpaqueData: e1.ExtraOpaqueData, @@ -94,6 +95,7 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, ChannelFlags: e2.ChannelFlags, TimeLockDelta: e2.TimeLockDelta, HtlcMinimumMsat: e2.MinHTLC, + HtlcMaximumMsat: e2.MaxHTLC, BaseFee: uint32(e2.FeeBaseMSat), FeeRate: uint32(e2.FeeProportionalMillionths), ExtraOpaqueData: e2.ExtraOpaqueData, From 207c4f030a1c1baebd4685306d4ff1ae14e77ffb Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 15/23] discovery/gossiper: include max HTLC when rebroadcasting stale channel updates Co-authored-by: Johan T. Halseth --- discovery/gossiper.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 1413f0bb..1cd405d0 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1321,6 +1321,17 @@ func (d *AuthenticatedGossiper) retransmitStaleChannels() error { return nil } + // If this edge has a ChannelUpdate that was created before the + // introduction of the MaxHTLC field, then we'll update this + // edge to propagate this information in the network. + if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc == 0 { + edgesToUpdate = append(edgesToUpdate, updateTuple{ + info: info, + edge: edge, + }) + return nil + } + const broadcastInterval = time.Hour * 24 timeElapsed := time.Since(edge.LastUpdate) @@ -2507,7 +2518,12 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, *lnwire.ChannelUpdate, error) { - var err error + // We'll make sure we support the new max_htlc field if not already + // present. + if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc == 0 { + edge.MessageFlags |= lnwire.ChanUpdateOptionMaxHtlc + edge.MaxHTLC = lnwire.NewMSatFromSatoshis(info.Capacity) + } // Make sure timestamp is always increased, such that our update gets // propagated. @@ -2516,6 +2532,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, timestamp = edge.LastUpdate.Unix() + 1 } edge.LastUpdate = time.Unix(timestamp, 0) + chanUpdate := &lnwire.ChannelUpdate{ ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID), @@ -2524,10 +2541,13 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, ChannelFlags: edge.ChannelFlags, TimeLockDelta: edge.TimeLockDelta, HtlcMinimumMsat: edge.MinHTLC, + HtlcMaximumMsat: edge.MaxHTLC, BaseFee: uint32(edge.FeeBaseMSat), FeeRate: uint32(edge.FeeProportionalMillionths), ExtraOpaqueData: edge.ExtraOpaqueData, } + + var err error chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(edge.SigBytes) if err != nil { return nil, nil, err From 390b9747932d531d42765a639856d2fdd24c804d Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 16/23] server: set max htlc in ChannelUpdate createChannelUpdate This method is called to convert an EdgePolicy to a ChannelUpdate. We make sure to carry over the max_htlc value. Co-authored-by: Johan T. Halseth --- server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server.go b/server.go index 7ef8a147..f12053c1 100644 --- a/server.go +++ b/server.go @@ -3104,6 +3104,7 @@ func createChannelUpdate(info *channeldb.ChannelEdgeInfo, ChannelFlags: policy.ChannelFlags, TimeLockDelta: policy.TimeLockDelta, HtlcMinimumMsat: policy.MinHTLC, + HtlcMaximumMsat: policy.MaxHTLC, BaseFee: uint32(policy.FeeBaseMSat), FeeRate: uint32(policy.FeeProportionalMillionths), ExtraOpaqueData: policy.ExtraOpaqueData, From cced7546598d0051c21235101adbb99c34825240 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 17/23] routing/router: when applying onion failure channel update, set max htlc In this commit, we ensure that when we update an edge as a result of a ChannelUpdate being returned from an onion failure, the max htlc portion of the channel update is included in the edge update. --- routing/router.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/router.go b/routing/router.go index 49038842..f69d817a 100644 --- a/routing/router.go +++ b/routing/router.go @@ -2079,6 +2079,7 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate, ChannelFlags: msg.ChannelFlags, TimeLockDelta: msg.TimeLockDelta, MinHTLC: msg.HtlcMinimumMsat, + MaxHTLC: msg.HtlcMaximumMsat, FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), }) From f8e588e2e3079d491371e8ae24b84911ce32a086 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 18/23] autopilot/graph: set max htlc in test edge policies Co-authored-by: Johan T. Halseth --- autopilot/graph.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/autopilot/graph.go b/autopilot/graph.go index 534f7d81..95bf89ad 100644 --- a/autopilot/graph.go +++ b/autopilot/graph.go @@ -227,9 +227,10 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, LastUpdate: time.Now(), TimeLockDelta: 10, MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(capacity), FeeBaseMSat: 10, FeeProportionalMillionths: 10000, - MessageFlags: 0, + MessageFlags: 1, ChannelFlags: 0, } @@ -242,9 +243,10 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, LastUpdate: time.Now(), TimeLockDelta: 10, MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(capacity), FeeBaseMSat: 10, FeeProportionalMillionths: 10000, - MessageFlags: 0, + MessageFlags: 1, ChannelFlags: 1, } if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { From 4aa52d267f000f84caf912c62fc14a5b8e7cacb5 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 19/23] funding+lnwallet: ensure max_htlc_value_in_flight smaller than capacity Return an error to the remote if larger. --- fundingmanager.go | 6 ++++-- lnwallet/errors.go | 10 ++++++++++ lnwallet/interface_test.go | 16 ++++++++++++---- lnwallet/reservation.go | 14 ++++++++++++-- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index f2591206..5d18a828 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1090,7 +1090,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs, CsvDelay: msg.CsvDelay, } - err = reservation.CommitConstraints(channelConstraints) + err = reservation.CommitConstraints(channelConstraints, amt) if err != nil { fndgLog.Errorf("Unacceptable channel constraints: %v", err) f.failFundingFlow(fmsg.peer, fmsg.msg.PendingChannelID, err) @@ -1254,7 +1254,9 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) { MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs, CsvDelay: msg.CsvDelay, } - err = resCtx.reservation.CommitConstraints(channelConstraints) + err = resCtx.reservation.CommitConstraints( + channelConstraints, resCtx.chanAmt, + ) if err != nil { fndgLog.Warnf("Unacceptable channel constraints: %v", err) f.failFundingFlow(fmsg.peer, fmsg.msg.PendingChannelID, err) diff --git a/lnwallet/errors.go b/lnwallet/errors.go index 79ab10f7..d72caf65 100644 --- a/lnwallet/errors.go +++ b/lnwallet/errors.go @@ -132,6 +132,16 @@ func ErrNumConfsTooLarge(numConfs, maxNumConfs uint32) error { } } +// ErrMaxValueInFlightTooLarge returns an error indicating that the 'max HTLC +// value in flight' the remote required is too large to be accepted. +func ErrMaxValueInFlightTooLarge(maxValInFlight, + maxMaxValInFlight lnwire.MilliSatoshi) ReservationError { + return ReservationError{ + fmt.Errorf("maxValueInFlight too large: %v, max is %v", + maxValInFlight, maxMaxValInFlight), + } +} + // ErrChanTooSmall returns an error indicating that an incoming channel request // was too small. We'll reject any incoming channels if they're below our // configured value for the min channel size we'll accept. diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 4549a266..7ed0dfe9 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -437,7 +437,9 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, CsvDelay: csvDelay, } - err = aliceChanReservation.CommitConstraints(channelConstraints) + err = aliceChanReservation.CommitConstraints( + channelConstraints, fundingAmount*2, + ) if err != nil { t.Fatalf("unable to verify constraints: %v", err) } @@ -471,7 +473,9 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, if err != nil { t.Fatalf("bob unable to init channel reservation: %v", err) } - err = bobChanReservation.CommitConstraints(channelConstraints) + err = bobChanReservation.CommitConstraints( + channelConstraints, fundingAmount*2, + ) if err != nil { t.Fatalf("unable to verify constraints: %v", err) } @@ -869,7 +873,9 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, CsvDelay: csvDelay, } - err = aliceChanReservation.CommitConstraints(channelConstraints) + err = aliceChanReservation.CommitConstraints( + channelConstraints, fundingAmt, + ) if err != nil { t.Fatalf("unable to verify constraints: %v", err) } @@ -903,7 +909,9 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to create bob reservation: %v", err) } - err = bobChanReservation.CommitConstraints(channelConstraints) + err = bobChanReservation.CommitConstraints( + channelConstraints, fundingAmt, + ) if err != nil { t.Fatalf("unable to verify constraints: %v", err) } diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index a32acfa2..1a684f25 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -286,7 +286,9 @@ func (r *ChannelReservation) SetNumConfsRequired(numConfs uint16) { // of satoshis that can be transferred in a single commitment. This function // will also attempt to verify the constraints for sanity, returning an error // if the parameters are seemed unsound. -func (r *ChannelReservation) CommitConstraints(c *channeldb.ChannelConstraints) error { +func (r *ChannelReservation) CommitConstraints(c *channeldb.ChannelConstraints, + capacity btcutil.Amount) error { + r.Lock() defer r.Unlock() @@ -341,7 +343,15 @@ func (r *ChannelReservation) CommitConstraints(c *channeldb.ChannelConstraints) ) } - // Our dust limit should always be less than or equal to our proposed + // Fail if the maxValueInFlight is greater than the channel capacity. + capacityMsat := lnwire.NewMSatFromSatoshis(capacity) + if c.MaxPendingAmount > capacityMsat { + return ErrMaxValueInFlightTooLarge( + c.MaxPendingAmount, capacityMsat, + ) + } + + // Our dust limit should always be less than or equal our proposed // channel reserve. if r.ourContribution.DustLimit > c.ChanReserve { r.ourContribution.DustLimit = c.ChanReserve From 4f9de9bf1df3d4ad9ca731c332c593972262cd1d Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 20/23] fundingmanager_test: add TestFundingManagerRejectInvalidMaxValueInFlight --- fundingmanager_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 21a522e1..efd9def8 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -2609,3 +2609,69 @@ func TestFundingManagerMaxConfs(t *testing.T) { string(err.Data)) } } + +// TestFundingManagerRejectInvalidMaxValueInFlight makes sure that the funding +// manager will act accordingly when the remote is requiring us to use a +// max_value_in_flight larger than the channel capacity. +func TestFundingManagerRejectInvalidMaxValueInFlight(t *testing.T) { + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) + defer tearDownFundingManagers(t, alice, bob) + + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + + // Make Alice require a max_htlc_value_in_flight greater than the + // channel capacity. + alice.fundingMgr.cfg.RequiredRemoteMaxValue = func( + _ btcutil.Amount) lnwire.MilliSatoshi { + return lnwire.NewMSatFromSatoshis(capacity) + 100 + } + + // Create a funding request and start the workflow. + updateChan := make(chan *lnrpc.OpenStatusUpdate) + errChan := make(chan error, 1) + initReq := &openChanReq{ + targetPubkey: bob.privKey.PubKey(), + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: 500000, + pushAmt: lnwire.NewMSatFromSatoshis(10), + private: true, + updates: updateChan, + err: errChan, + } + + alice.fundingMgr.initFundingWorkflow(bob, initReq) + + // Alice should have sent the OpenChannel message to Bob. + var aliceMsg lnwire.Message + select { + case aliceMsg = <-alice.msgChan: + case err := <-initReq.err: + t.Fatalf("error init funding workflow: %v", err) + case <-time.After(time.Second * 5): + t.Fatalf("alice did not send OpenChannel message") + } + + openChannelReq, ok := aliceMsg.(*lnwire.OpenChannel) + if !ok { + errorMsg, gotError := aliceMsg.(*lnwire.Error) + if gotError { + t.Fatalf("expected OpenChannel to be sent "+ + "from bob, instead got error: %v", + lnwire.ErrorCode(errorMsg.Data[0])) + } + t.Fatalf("expected OpenChannel to be sent from "+ + "alice, instead got %T", aliceMsg) + } + + // Let Bob handle the init message. + bob.fundingMgr.processFundingOpen(openChannelReq, alice) + + // Assert Bob responded with an ErrMaxValueInFlightTooLarge error. + err := assertFundingMsgSent(t, bob.msgChan, "Error").(*lnwire.Error) + if !strings.Contains(string(err.Data), "maxValueInFlight too large") { + t.Fatalf("expected ErrMaxValueInFlightTooLarge error, "+ + "got \"%v\"", string(err.Data)) + } +} From a66a1e113fb04912ac2e8ae62446bce8fed8d12a Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:45 +0100 Subject: [PATCH 21/23] fundingmanager: when funding new channels, set a default max htlc In this commit, we set a default max HTLC value in ChannelUpdates sent out for newly funded channels. As a result, we also default to setting `MessageFlags` equal to 1 in each new ChannelUpdate, since the max HTLC field is an optional field and MessageFlags indicates the presence of optional fields within the ChannelUpdate. For a default max HTLC, we choose the maximum msats worth of HTLCs that can be pending (or in-flight) on our side of the channel. The reason for this is because the spec specifies that the max HTLC present in a ChannelUpdate must be less than or equal to both total channel capacity and the maximum in-flight amount set by the peer. Since this in-flight value will always be less than or equal to channel capacity, it is a safe spec-compliant default. Co-authored-by: Johan T. Halseth --- fundingmanager.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index 5d18a828..772cc319 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -2111,11 +2111,18 @@ func (f *fundingManager) addToRouterGraph(completeChan *channeldb.OpenChannel, // need to determine the smallest HTLC it deems economically relevant. fwdMinHTLC := completeChan.LocalChanCfg.MinHTLC + // We'll obtain the max HTLC value we can forward in our direction, as + // we'll use this value within our ChannelUpdate. This value must be <= + // channel capacity and <= the maximum in-flight msats set by the peer, so + // we default to max in-flight msats as this value will always be <= + // channel capacity. + fwdMaxHTLC := completeChan.LocalChanCfg.MaxPendingAmount + ann, err := f.newChanAnnouncement( f.cfg.IDKey, completeChan.IdentityPub, completeChan.LocalChanCfg.MultiSigKey.PubKey, completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, - chanID, fwdMinHTLC, + chanID, fwdMinHTLC, fwdMaxHTLC, ) if err != nil { return fmt.Errorf("error generating channel "+ @@ -2280,13 +2287,20 @@ func (f *fundingManager) annAfterSixConfs(completeChan *channeldb.OpenChannel, // HTLC it deems economically relevant. fwdMinHTLC := completeChan.LocalChanCfg.MinHTLC + // We'll obtain the max HTLC value we can forward in our direction, as + // we'll use this value within our ChannelUpdate. This value must be <= + // channel capacity and <= the maximum in-flight msats set by the peer, + // so we default to max in-flight msats as this value will always be <= + // channel capacity. + fwdMaxHTLC := completeChan.LocalChanCfg.MaxPendingAmount + // Create and broadcast the proofs required to make this channel // public and usable for other nodes for routing. err = f.announceChannel( f.cfg.IDKey, completeChan.IdentityPub, completeChan.LocalChanCfg.MultiSigKey.PubKey, completeChan.RemoteChanCfg.MultiSigKey.PubKey, - *shortChanID, chanID, fwdMinHTLC, + *shortChanID, chanID, fwdMinHTLC, fwdMaxHTLC, ) if err != nil { return fmt.Errorf("channel announcement failed: %v", err) @@ -2453,7 +2467,7 @@ type chanAnnouncement struct { func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, localFundingKey, remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, chanID lnwire.ChannelID, - fwdMinHTLC lnwire.MilliSatoshi) (*chanAnnouncement, error) { + fwdMinHTLC, fwdMaxHTLC lnwire.MilliSatoshi) (*chanAnnouncement, error) { chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash @@ -2498,13 +2512,17 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, chanFlags = 1 } + // Our channel update message flags will signal that we support the + // max_htlc field. + msgFlags := lnwire.ChanUpdateOptionMaxHtlc + // We announce the channel with the default values. Some of // these values can later be changed by crafting a new ChannelUpdate. chanUpdateAnn := &lnwire.ChannelUpdate{ ShortChannelID: shortChanID, ChainHash: chainHash, Timestamp: uint32(time.Now().Unix()), - MessageFlags: 0, + MessageFlags: msgFlags, ChannelFlags: chanFlags, TimeLockDelta: uint16(f.cfg.DefaultRoutingPolicy.TimeLockDelta), @@ -2512,6 +2530,7 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, // to use, as our ChannelUpdate will be used to carry HTLCs // towards them. HtlcMinimumMsat: fwdMinHTLC, + HtlcMaximumMsat: fwdMaxHTLC, BaseFee: uint32(f.cfg.DefaultRoutingPolicy.BaseFee), FeeRate: uint32(f.cfg.DefaultRoutingPolicy.FeeRate), @@ -2590,7 +2609,7 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, // finish, either successfully or with an error. func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKey, remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, - chanID lnwire.ChannelID, fwdMinHTLC lnwire.MilliSatoshi) error { + chanID lnwire.ChannelID, fwdMinHTLC, fwdMaxHTLC lnwire.MilliSatoshi) error { // First, we'll create the batch of announcements to be sent upon // initial channel creation. This includes the channel announcement @@ -2598,7 +2617,7 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe // proof needed to fully authenticate the channel. ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey, localFundingKey, remoteFundingKey, shortChanID, chanID, - fwdMinHTLC, + fwdMinHTLC, fwdMaxHTLC, ) if err != nil { fndgLog.Errorf("can't generate channel announcement: %v", err) From 4fb1536f549a8c220c3c4c9426d65a1924b24ef7 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:46 +0100 Subject: [PATCH 22/23] fundingmanager_test: verify max HTLC in ChannelUpdates In this commit, we verify that ChannelUpdates for newly funded channels contain the max HTLC that we expect. We expect the max HTLC value of each ChannelUpdate to equal the maximum pending msats in HTLCs required by the remote peer. Co-authored-by: Johan T. Halseth --- fundingmanager_test.go | 104 ++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/fundingmanager_test.go b/fundingmanager_test.go index efd9def8..5802083c 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -433,6 +433,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) { FeeRate: 1000, TimeLockDelta: 10, }, + RequiredRemoteMaxValue: oldCfg.RequiredRemoteMaxValue, PublishTransaction: func(txn *wire.MsgTx) error { publishChan <- txn return nil @@ -819,7 +820,8 @@ func assertAddedToRouterGraph(t *testing.T, alice, bob *testNode, // advertised value will be checked against the other node's default min_htlc // value. func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, - customMinHtlc ...lnwire.MilliSatoshi) { + capacity btcutil.Amount, customMinHtlc ...lnwire.MilliSatoshi) { + t.Helper() // After the FundingLocked message is sent, Alice and Bob will each // send the following messages to their gossiper: @@ -871,6 +873,24 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, minHtlc, m.HtlcMinimumMsat) } + // The MaxHTLC value should at this point + // _always_ be the same as the + // maxValueInFlight capacity. + if m.MessageFlags != 1 { + t.Fatalf("expected message flags to "+ + "be 1, was %v", m.MessageFlags) + } + + maxPendingMsat := alice.fundingMgr.cfg.RequiredRemoteMaxValue( + capacity, + ) + if maxPendingMsat != m.HtlcMaximumMsat { + t.Fatalf("expected ChannelUpdate to "+ + "advertise max HTLC %v, had %v", + maxPendingMsat, + m.HtlcMaximumMsat) + } + gotChannelUpdate = true } } @@ -1005,8 +1025,11 @@ func TestFundingManagerNormalWorkflow(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - true) + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, true) // Check that neither Alice nor Bob sent an error message. assertErrorNotSent(t, alice.msgChan) @@ -1037,7 +1060,7 @@ func TestFundingManagerNormalWorkflow(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Check that the state machine is updated accordingly assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) @@ -1072,9 +1095,12 @@ func TestFundingManagerRestartBehavior(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt updateChan := make(chan *lnrpc.OpenStatusUpdate) - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - true) + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, true) // After the funding transaction gets mined, both nodes will send the // fundingLocked message to the other peer. If the funding node fails @@ -1175,7 +1201,7 @@ func TestFundingManagerRestartBehavior(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Check that the state machine is updated accordingly assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) @@ -1207,9 +1233,12 @@ func TestFundingManagerOfflinePeer(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt updateChan := make(chan *lnrpc.OpenStatusUpdate) - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - true) + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, true) // After the funding transaction gets mined, both nodes will send the // fundingLocked message to the other peer. If the funding node fails @@ -1299,7 +1328,7 @@ func TestFundingManagerOfflinePeer(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Check that the state machine is updated accordingly assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) @@ -1656,8 +1685,11 @@ func TestFundingManagerReceiveFundingLockedTwice(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - true) + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, true) // Notify that transaction was mined alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} @@ -1684,7 +1716,7 @@ func TestFundingManagerReceiveFundingLockedTwice(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Check that the state machine is updated accordingly assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) @@ -1745,8 +1777,11 @@ func TestFundingManagerRestartAfterChanAnn(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - true) + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, true) // Notify that transaction was mined alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} @@ -1773,7 +1808,7 @@ func TestFundingManagerRestartAfterChanAnn(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Check that the state machine is updated accordingly assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) @@ -1819,8 +1854,11 @@ func TestFundingManagerRestartAfterReceivingFundingLocked(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - true) + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, true) // Notify that transaction was mined alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} @@ -1860,7 +1898,7 @@ func TestFundingManagerRestartAfterReceivingFundingLocked(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Check that the state machine is updated accordingly assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) @@ -1889,8 +1927,11 @@ func TestFundingManagerPrivateChannel(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - false) + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, false) // Notify that transaction was mined alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} @@ -1917,7 +1958,7 @@ func TestFundingManagerPrivateChannel(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // The funding transaction is now confirmed, wait for the // OpenStatusUpdate_ChanOpen update @@ -1988,8 +2029,11 @@ func TestFundingManagerPrivateRestart(t *testing.T) { // Run through the process of opening the channel, up until the funding // transaction is broadcasted. - fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, - false) + localAmt := btcutil.Amount(500000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1, + updateChan, false) // Notify that transaction was mined alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} @@ -2016,7 +2060,7 @@ func TestFundingManagerPrivateRestart(t *testing.T) { // Make sure both fundingManagers send the expected channel // announcements. - assertChannelAnnouncements(t, alice, bob) + assertChannelAnnouncements(t, alice, bob, capacity) // Note: We don't check for the addedToRouterGraph state because in // the private channel mode, the state is quickly changed from @@ -2110,14 +2154,18 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) { // needed. updateChan := make(chan *lnrpc.OpenStatusUpdate) + localAmt := btcutil.Amount(5000000) + pushAmt := btcutil.Amount(0) + capacity := localAmt + pushAmt + // Create a funding request with the custom parameters and start the // workflow. errChan := make(chan error, 1) initReq := &openChanReq{ targetPubkey: bob.privKey.PubKey(), chainHash: *activeNetParams.GenesisHash, - localFundingAmt: 5000000, - pushAmt: lnwire.NewMSatFromSatoshis(0), + localFundingAmt: localAmt, + pushAmt: lnwire.NewMSatFromSatoshis(pushAmt), private: false, minHtlc: minHtlc, remoteCsvDelay: csvDelay, @@ -2313,7 +2361,7 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) { // announcements. Alice should advertise the default MinHTLC value of // 5, while bob should advertise the value minHtlc, since Alice // required him to use it. - assertChannelAnnouncements(t, alice, bob, 5, minHtlc) + assertChannelAnnouncements(t, alice, bob, capacity, 5, minHtlc) // The funding transaction is now confirmed, wait for the // OpenStatusUpdate_ChanOpen update From 7d34ce9d08e0aa0139a3d899a29e50ef9634b464 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 16 Jan 2019 12:43:46 +0100 Subject: [PATCH 23/23] lnwire+multi: define HasMaxHtlc helper on msgFlags --- channeldb/graph.go | 4 ++-- discovery/gossiper.go | 4 ++-- lnwire/channel_update.go | 12 +++++++++--- routing/ann_validation.go | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/channeldb/graph.go b/channeldb/graph.go index 19a19439..dba5108c 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -3372,7 +3372,7 @@ func serializeChanEdgePolicy(w io.Writer, edge *ChannelEdgePolicy, // of the opaque data. // TODO(halseth): clean up when moving to TLV. var opaqueBuf bytes.Buffer - if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc != 0 { + if edge.MessageFlags.HasMaxHtlc() { err := binary.Write(&opaqueBuf, byteOrder, uint64(edge.MaxHTLC)) if err != nil { return err @@ -3465,7 +3465,7 @@ func deserializeChanEdgePolicy(r io.Reader, } // See if optional fields are present. - if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc != 0 { + if edge.MessageFlags.HasMaxHtlc() { // The max_htlc field should be at the beginning of the opaque // bytes. opq := edge.ExtraOpaqueData diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 1cd405d0..a8f15440 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1324,7 +1324,7 @@ func (d *AuthenticatedGossiper) retransmitStaleChannels() error { // If this edge has a ChannelUpdate that was created before the // introduction of the MaxHTLC field, then we'll update this // edge to propagate this information in the network. - if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc == 0 { + if !edge.MessageFlags.HasMaxHtlc() { edgesToUpdate = append(edgesToUpdate, updateTuple{ info: info, edge: edge, @@ -2520,7 +2520,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, // We'll make sure we support the new max_htlc field if not already // present. - if edge.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc == 0 { + if !edge.MessageFlags.HasMaxHtlc() { edge.MessageFlags |= lnwire.ChanUpdateOptionMaxHtlc edge.MaxHTLC = lnwire.NewMSatFromSatoshis(info.Capacity) } diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 1788cdc7..fd627646 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -24,6 +24,12 @@ func (c ChanUpdateMsgFlags) String() string { return fmt.Sprintf("%08b", c) } +// HasMaxHtlc returns true if the htlc_maximum_msat option bit is set in the +// message flags. +func (c ChanUpdateMsgFlags) HasMaxHtlc() bool { + return c&ChanUpdateOptionMaxHtlc != 0 +} + // ChanUpdateChanFlags is a bitfield that signals various options concerning a // particular channel edge. Each bit is to be examined in order to determine // how the ChannelUpdate message is to be interpreted. @@ -139,7 +145,7 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { } // Now check whether the max HTLC field is present and read it if so. - if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 { + if a.MessageFlags.HasMaxHtlc() { if err := ReadElements(r, &a.HtlcMaximumMsat); err != nil { return err } @@ -183,7 +189,7 @@ func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error { // Now append optional fields if they are set. Currently, the only // optional field is max HTLC. - if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 { + if a.MessageFlags.HasMaxHtlc() { if err := WriteElements(w, a.HtlcMaximumMsat); err != nil { return err } @@ -232,7 +238,7 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) { // Now append optional fields if they are set. Currently, the only // optional field is max HTLC. - if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 { + if a.MessageFlags.HasMaxHtlc() { if err := WriteElements(&w, a.HtlcMaximumMsat); err != nil { return nil, err } diff --git a/routing/ann_validation.go b/routing/ann_validation.go index 5c70dd86..4b304b5a 100644 --- a/routing/ann_validation.go +++ b/routing/ann_validation.go @@ -156,7 +156,7 @@ func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, func validateOptionalFields(capacity btcutil.Amount, msg *lnwire.ChannelUpdate) error { - if msg.MessageFlags&lnwire.ChanUpdateOptionMaxHtlc != 0 { + if msg.MessageFlags.HasMaxHtlc() { maxHtlc := msg.HtlcMaximumMsat if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat { return errors.Errorf("invalid max htlc for channel "+