diff --git a/autopilot/graph.go b/autopilot/graph.go index 15440caf..95bf89ad 100644 --- a/autopilot/graph.go +++ b/autopilot/graph.go @@ -227,9 +227,11 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, LastUpdate: time.Now(), TimeLockDelta: 10, MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(capacity), FeeBaseMSat: 10, FeeProportionalMillionths: 10000, - Flags: 0, + MessageFlags: 1, + ChannelFlags: 0, } if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { @@ -241,9 +243,11 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, LastUpdate: time.Now(), TimeLockDelta: 10, MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(capacity), FeeBaseMSat: 10, FeeProportionalMillionths: 10000, - Flags: 1, + MessageFlags: 1, + ChannelFlags: 1, } if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { return nil, nil, err 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 849afd9f..dba5108c 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 } @@ -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 @@ -2435,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 @@ -3169,54 +3177,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.Flags); 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) @@ -3235,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 } @@ -3297,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, @@ -3341,6 +3325,73 @@ 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 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.HasMaxHtlc() { + 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, opaqueBuf.Bytes()); err != nil { + return err + } + return nil +} + func deserializeChanEdgePolicy(r io.Reader, nodes *bbolt.Bucket) (*ChannelEdgePolicy, error) { @@ -3363,7 +3414,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 { @@ -3396,6 +3450,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. @@ -3409,6 +3464,25 @@ func deserializeChanEdgePolicy(r io.Reader, return nil, err } - edge.Node = &node + // See if optional fields are present. + if edge.MessageFlags.HasMaxHtlc() { + // 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 } diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index cbd78721..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,19 +664,16 @@ 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, LastUpdate: time.Unix(433453, 0), - Flags: 0, + MessageFlags: 1, + ChannelFlags: 0, TimeLockDelta: 99, MinHTLC: 2342135, + MaxHTLC: 13928598, FeeBaseMSat: 4352345, FeeProportionalMillionths: 3452352, Node: secondNode, @@ -712,9 +684,11 @@ func TestEdgeInfoUpdates(t *testing.T) { SigBytes: testSig.Serialize(), ChannelID: chanID, LastUpdate: time.Unix(124234, 0), - Flags: 1, + MessageFlags: 1, + ChannelFlags: 1, TimeLockDelta: 99, MinHTLC: 2342135, + MaxHTLC: 13928598, FeeBaseMSat: 4352345, FeeProportionalMillionths: 90392423, Node: firstNode, @@ -722,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) } @@ -792,8 +806,11 @@ func newEdgePolicy(chanID uint64, op wire.OutPoint, db *DB, return &ChannelEdgePolicy{ ChannelID: chanID, LastUpdate: time.Unix(updateTime, 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, @@ -894,7 +911,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 +921,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 +1162,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 +1172,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 +1431,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 +1441,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 +1932,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 +1942,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 +2070,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 +2085,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 +2142,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 +2150,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 +2207,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 +2299,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 { @@ -2594,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. @@ -2645,9 +2829,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, "+ @@ -2657,6 +2845,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) diff --git a/discovery/chan_series.go b/discovery/chan_series.go index 25ecbed4..b7b77883 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -327,9 +327,11 @@ 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, + HtlcMaximumMsat: e1.MaxHTLC, BaseFee: uint32(e1.FeeBaseMSat), FeeRate: uint32(e1.FeeProportionalMillionths), ExtraOpaqueData: e1.ExtraOpaqueData, @@ -346,9 +348,11 @@ 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, + HtlcMaximumMsat: e2.MaxHTLC, BaseFee: uint32(e2.FeeBaseMSat), FeeRate: uint32(e2.FeeProportionalMillionths), ExtraOpaqueData: e2.ExtraOpaqueData, diff --git a/discovery/gossiper.go b/discovery/gossiper.go index b74a2f65..a8f15440 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) @@ -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.HasMaxHtlc() { + edgesToUpdate = append(edgesToUpdate, updateTuple{ + info: info, + edge: edge, + }) + return nil + } + const broadcastInterval = time.Hour * 24 timeElapsed := time.Since(edge.LastUpdate) @@ -1911,7 +1922,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,16 +1997,17 @@ 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() } - // 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) @@ -2009,9 +2021,11 @@ 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, + MaxHTLC: msg.HtlcMaximumMsat, FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), ExtraOpaqueData: msg.ExtraOpaqueData, @@ -2041,9 +2055,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() } @@ -2504,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.HasMaxHtlc() { + edge.MessageFlags |= lnwire.ChanUpdateOptionMaxHtlc + edge.MaxHTLC = lnwire.NewMSatFromSatoshis(info.Capacity) + } // Make sure timestamp is always increased, such that our update gets // propagated. @@ -2513,17 +2532,22 @@ 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), Timestamp: uint32(timestamp), - Flags: edge.Flags, + MessageFlags: edge.MessageFlags, + 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 @@ -2546,7 +2570,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 a1db0e70..be029878 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -10,6 +10,7 @@ import ( "net" "os" "reflect" + "strings" "sync" "testing" "time" @@ -17,6 +18,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 +56,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 +133,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 } @@ -258,7 +265,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 +274,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,20 +447,27 @@ 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) { var err error + htlcMinMsat := lnwire.MilliSatoshi(prand.Int63()) a := &lnwire.ChannelUpdate{ ShortChannelID: lnwire.ShortChannelID{ BlockHeight: blockHeight, }, Timestamp: timestamp, + MessageFlags: lnwire.ChanUpdateOptionMaxHtlc, + ChannelFlags: flags, TimeLockDelta: uint16(prand.Int63()), - Flags: flags, - 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()), } @@ -461,14 +475,7 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, 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 } @@ -476,6 +483,22 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, 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 { @@ -2028,7 +2051,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) { assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) { channelKey := channelUpdateID{ ua3.ShortChannelID, - ua3.Flags, + ua3.ChannelFlags, } mws, ok := announcements.channelUpdates[channelKey] @@ -2752,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/discovery/utils.go b/discovery/utils.go index 63143c86..3c0fdc17 100644 --- a/discovery/utils.go +++ b/discovery/utils.go @@ -72,9 +72,11 @@ 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, + HtlcMaximumMsat: e1.MaxHTLC, BaseFee: uint32(e1.FeeBaseMSat), FeeRate: uint32(e1.FeeProportionalMillionths), ExtraOpaqueData: e1.ExtraOpaqueData, @@ -89,9 +91,11 @@ 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, + HtlcMaximumMsat: e2.MaxHTLC, BaseFee: uint32(e2.FeeBaseMSat), FeeRate: uint32(e2.FeeProportionalMillionths), ExtraOpaqueData: e2.ExtraOpaqueData, diff --git a/fundingmanager.go b/fundingmanager.go index a5914f29..772cc319 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) @@ -2109,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 "+ @@ -2278,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) @@ -2451,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 @@ -2468,7 +2484,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 @@ -2496,19 +2512,25 @@ 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()), - Flags: chanFlags, + MessageFlags: msgFlags, + ChannelFlags: chanFlags, TimeLockDelta: uint16(f.cfg.DefaultRoutingPolicy.TimeLockDelta), // We use the HtlcMinimumMsat that the remote party required us // 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), @@ -2587,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 @@ -2595,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) diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 21a522e1..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 @@ -2609,3 +2657,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)) + } +} 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 diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index af7fbd28..fd627646 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -2,22 +2,44 @@ package lnwire import ( "bytes" + "fmt" "io" "io/ioutil" "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 + +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) +} + +// 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. -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 @@ -25,6 +47,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 @@ -48,13 +75,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 @@ -75,6 +107,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 @@ -98,7 +133,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, @@ -108,6 +144,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.HasMaxHtlc() { + 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 @@ -128,18 +171,32 @@ 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, a.Timestamp, - a.Flags, + a.MessageFlags, + a.ChannelFlags, a.TimeLockDelta, 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.HasMaxHtlc() { + 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 @@ -168,16 +225,29 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) { a.ChainHash[:], a.ShortChannelID, a.Timestamp, - a.Flags, + a.MessageFlags, + a.ChannelFlags, a.TimeLockDelta, 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.HasMaxHtlc() { + 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.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..3046c2f3 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() @@ -605,12 +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()), - Flags: ChanUpdateFlag(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()), } 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/ann_validation.go b/routing/ann_validation.go index 257ac3fd..4b304b5a 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.HasMaxHtlc() { + 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/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..f69d817a 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) } } @@ -2059,18 +2060,26 @@ 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), - Flags: msg.Flags, + MessageFlags: msg.MessageFlags, + ChannelFlags: msg.ChannelFlags, TimeLockDelta: msg.TimeLockDelta, MinHTLC: msg.HtlcMinimumMsat, + MaxHTLC: msg.HtlcMaximumMsat, FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), }) @@ -2270,7 +2279,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 83dccc39..f992023d 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3780,7 +3780,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, } } @@ -3790,7 +3790,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..f12053c1 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,9 +3100,11 @@ 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, + HtlcMaximumMsat: policy.MaxHTLC, BaseFee: uint32(policy.FeeBaseMSat), FeeRate: uint32(policy.FeeProportionalMillionths), ExtraOpaqueData: policy.ExtraOpaqueData,