Merge pull request #2460 from halseth/max-htlc-size-pickup

Support the max_htlc field from ChannelUpdates
This commit is contained in:
Olaoluwa Osuntokun 2019-01-22 18:54:38 -08:00 committed by GitHub
commit e2285732e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1027 additions and 260 deletions

@ -227,9 +227,11 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
LastUpdate: time.Now(), LastUpdate: time.Now(),
TimeLockDelta: 10, TimeLockDelta: 10,
MinHTLC: 1, MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
Flags: 0, MessageFlags: 1,
ChannelFlags: 0,
} }
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
@ -241,9 +243,11 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
LastUpdate: time.Now(), LastUpdate: time.Now(),
TimeLockDelta: 10, TimeLockDelta: 10,
MinHTLC: 1, MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
Flags: 1, MessageFlags: 1,
ChannelFlags: 1,
} }
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil { if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
return nil, nil, err return nil, nil, err

@ -97,6 +97,12 @@ var (
// ErrNoForwardingEvents is returned in the case that a query fails due // ErrNoForwardingEvents is returned in the case that a query fails due
// to the log not having any recorded events. // to the log not having any recorded events.
ErrNoForwardingEvents = fmt.Errorf("no recorded forwarding 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 // ErrTooManyExtraOpaqueBytes creates an error which should be returned if the

@ -1640,7 +1640,7 @@ func delChannelByEdge(edges *bbolt.Bucket, edgeIndex *bbolt.Bucket,
func (c *ChannelGraph) UpdateEdgePolicy(edge *ChannelEdgePolicy) error { func (c *ChannelGraph) UpdateEdgePolicy(edge *ChannelEdgePolicy) error {
return c.db.Update(func(tx *bbolt.Tx) error { return c.db.Update(func(tx *bbolt.Tx) error {
edges := tx.Bucket(edgeBucket) edges := tx.Bucket(edgeBucket)
if edge == nil { if edges == nil {
return ErrEdgeNotFound return ErrEdgeNotFound
} }
@ -1677,7 +1677,7 @@ func updateEdgePolicy(edges, edgeIndex, nodes *bbolt.Bucket,
// Depending on the flags value passed above, either the first // Depending on the flags value passed above, either the first
// or second edge policy is being updated. // or second edge policy is being updated.
var fromNode, toNode []byte var fromNode, toNode []byte
if edge.Flags&lnwire.ChanUpdateDirection == 0 { if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
fromNode = nodeInfo[:33] fromNode = nodeInfo[:33]
toNode = nodeInfo[33:66] toNode = nodeInfo[33:66]
} else { } else {
@ -2422,9 +2422,13 @@ type ChannelEdgePolicy struct {
// was received. // was received.
LastUpdate time.Time LastUpdate time.Time
// Flags is a bitfield which signals the capabilities of the channel as // MessageFlags is a bitfield which indicates the presence of optional
// well as the directed edge this update applies to. // fields (like max_htlc) in the policy.
Flags lnwire.ChanUpdateFlag 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 // TimeLockDelta is the number of blocks this node will subtract from
// the expiry of an incoming HTLC. This value expresses the time buffer // the expiry of an incoming HTLC. This value expresses the time buffer
@ -2435,6 +2439,10 @@ type ChannelEdgePolicy struct {
// in millisatoshi. // in millisatoshi.
MinHTLC lnwire.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 // FeeBaseMSat is the base HTLC fee that will be charged for forwarding
// ANY HTLC, expressed in mSAT's. // ANY HTLC, expressed in mSAT's.
FeeBaseMSat lnwire.MilliSatoshi FeeBaseMSat lnwire.MilliSatoshi
@ -3169,54 +3177,15 @@ func putChanEdgePolicy(edges, nodes *bbolt.Bucket, edge *ChannelEdgePolicy,
byteOrder.PutUint64(edgeKey[33:], edge.ChannelID) byteOrder.PutUint64(edgeKey[33:], edge.ChannelID)
var b bytes.Buffer var b bytes.Buffer
if err := serializeChanEdgePolicy(&b, edge, to); err != nil {
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 {
return err return err
} }
// Before we write out the new edge, we'll create a new entry in the // Before we write out the new edge, we'll create a new entry in the
// update index in order to keep it fresh. // update index in order to keep it fresh.
updateUnix := uint64(edge.LastUpdate.Unix())
var indexKey [8 + 8]byte var indexKey [8 + 8]byte
copy(indexKey[:], scratch[:]) byteOrder.PutUint64(indexKey[:8], updateUnix)
byteOrder.PutUint64(indexKey[8:], edge.ChannelID) byteOrder.PutUint64(indexKey[8:], edge.ChannelID)
updateIndex, err := edges.CreateBucketIfNotExists(edgeUpdateIndexBucket) 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 // *prior* update time in order to delete it. To do this, we'll
// need to deserialize the existing policy within the database // need to deserialize the existing policy within the database
// (now outdated by the new one), and delete its corresponding // (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( oldEdgePolicy, err := deserializeChanEdgePolicy(
bytes.NewReader(edgeBytes), nodes, bytes.NewReader(edgeBytes), nodes,
) )
if err != nil { if err != nil && err != ErrEdgePolicyOptionalFieldNotFound {
return err return err
} }
@ -3297,7 +3270,18 @@ func fetchChanEdgePolicy(edges *bbolt.Bucket, chanID []byte,
edgeReader := bytes.NewReader(edgeBytes) 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, func fetchChanEdgePolicies(edgeIndex *bbolt.Bucket, edges *bbolt.Bucket,
@ -3341,6 +3325,73 @@ func fetchChanEdgePolicies(edgeIndex *bbolt.Bucket, edges *bbolt.Bucket,
return edge1, edge2, nil 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, func deserializeChanEdgePolicy(r io.Reader,
nodes *bbolt.Bucket) (*ChannelEdgePolicy, error) { nodes *bbolt.Bucket) (*ChannelEdgePolicy, error) {
@ -3363,7 +3414,10 @@ func deserializeChanEdgePolicy(r io.Reader,
unix := int64(byteOrder.Uint64(scratch[:])) unix := int64(byteOrder.Uint64(scratch[:]))
edge.LastUpdate = time.Unix(unix, 0) 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 return nil, err
} }
if err := binary.Read(r, byteOrder, &edge.TimeLockDelta); err != nil { 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", return nil, fmt.Errorf("unable to fetch node: %x, %v",
pub[:], err) pub[:], err)
} }
edge.Node = &node
// We'll try and see if there are any opaque bytes left, if not, then // 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. // we'll ignore the EOF error and return the edge as is.
@ -3409,6 +3464,25 @@ func deserializeChanEdgePolicy(r io.Reader,
return nil, err 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 return edge, nil
} }

@ -622,33 +622,8 @@ func assertEdgeInfoEqual(t *testing.T, e1 *ChannelEdgeInfo,
} }
} }
func TestEdgeInfoUpdates(t *testing.T) { func createChannelEdge(db *DB, node1, node2 *LightningNode) (*ChannelEdgeInfo,
t.Parallel() *ChannelEdgePolicy, *ChannelEdgePolicy) {
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)
}
var ( var (
firstNode *LightningNode firstNode *LightningNode
@ -689,19 +664,16 @@ func TestEdgeInfoUpdates(t *testing.T) {
copy(edgeInfo.NodeKey2Bytes[:], secondNode.PubKeyBytes[:]) copy(edgeInfo.NodeKey2Bytes[:], secondNode.PubKeyBytes[:])
copy(edgeInfo.BitcoinKey1Bytes[:], firstNode.PubKeyBytes[:]) copy(edgeInfo.BitcoinKey1Bytes[:], firstNode.PubKeyBytes[:])
copy(edgeInfo.BitcoinKey2Bytes[:], secondNode.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{ edge1 := &ChannelEdgePolicy{
SigBytes: testSig.Serialize(), SigBytes: testSig.Serialize(),
ChannelID: chanID, ChannelID: chanID,
LastUpdate: time.Unix(433453, 0), LastUpdate: time.Unix(433453, 0),
Flags: 0, MessageFlags: 1,
ChannelFlags: 0,
TimeLockDelta: 99, TimeLockDelta: 99,
MinHTLC: 2342135, MinHTLC: 2342135,
MaxHTLC: 13928598,
FeeBaseMSat: 4352345, FeeBaseMSat: 4352345,
FeeProportionalMillionths: 3452352, FeeProportionalMillionths: 3452352,
Node: secondNode, Node: secondNode,
@ -712,9 +684,11 @@ func TestEdgeInfoUpdates(t *testing.T) {
SigBytes: testSig.Serialize(), SigBytes: testSig.Serialize(),
ChannelID: chanID, ChannelID: chanID,
LastUpdate: time.Unix(124234, 0), LastUpdate: time.Unix(124234, 0),
Flags: 1, MessageFlags: 1,
ChannelFlags: 1,
TimeLockDelta: 99, TimeLockDelta: 99,
MinHTLC: 2342135, MinHTLC: 2342135,
MaxHTLC: 13928598,
FeeBaseMSat: 4352345, FeeBaseMSat: 4352345,
FeeProportionalMillionths: 90392423, FeeProportionalMillionths: 90392423,
Node: firstNode, Node: firstNode,
@ -722,8 +696,48 @@ func TestEdgeInfoUpdates(t *testing.T) {
db: db, db: db,
} }
// Next, insert both nodes into the database, they should both be return edgeInfo, edge1, edge2
// inserted without any issues. }
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 { if err := graph.UpdateEdgePolicy(edge1); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
@ -792,8 +806,11 @@ func newEdgePolicy(chanID uint64, op wire.OutPoint, db *DB,
return &ChannelEdgePolicy{ return &ChannelEdgePolicy{
ChannelID: chanID, ChannelID: chanID,
LastUpdate: time.Unix(updateTime, 0), LastUpdate: time.Unix(updateTime, 0),
MessageFlags: 1,
ChannelFlags: 0,
TimeLockDelta: uint16(prand.Int63()), TimeLockDelta: uint16(prand.Int63()),
MinHTLC: lnwire.MilliSatoshi(prand.Int63()), MinHTLC: lnwire.MilliSatoshi(prand.Int63()),
MaxHTLC: lnwire.MilliSatoshi(prand.Int63()),
FeeBaseMSat: lnwire.MilliSatoshi(prand.Int63()), FeeBaseMSat: lnwire.MilliSatoshi(prand.Int63()),
FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int63()), FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int63()),
db: db, db: db,
@ -894,7 +911,7 @@ func TestGraphTraversal(t *testing.T) {
// Create and add an edge with random data that points from // Create and add an edge with random data that points from
// node1 -> node2. // node1 -> node2.
edge := randEdgePolicy(chanID, op, db) edge := randEdgePolicy(chanID, op, db)
edge.Flags = 0 edge.ChannelFlags = 0
edge.Node = secondNode edge.Node = secondNode
edge.SigBytes = testSig.Serialize() edge.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge); err != nil { 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 // Create another random edge that points from node2 -> node1
// this time. // this time.
edge = randEdgePolicy(chanID, op, db) edge = randEdgePolicy(chanID, op, db)
edge.Flags = 1 edge.ChannelFlags = 1
edge.Node = firstNode edge.Node = firstNode
edge.SigBytes = testSig.Serialize() edge.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge); err != nil { 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 // Create and add an edge with random data that points from
// node_i -> node_i+1 // node_i -> node_i+1
edge := randEdgePolicy(chanID, op, db) edge := randEdgePolicy(chanID, op, db)
edge.Flags = 0 edge.ChannelFlags = 0
edge.Node = graphNodes[i] edge.Node = graphNodes[i]
edge.SigBytes = testSig.Serialize() edge.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge); err != nil { 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 -> // Create another random edge that points from node_i+1 ->
// node_i this time. // node_i this time.
edge = randEdgePolicy(chanID, op, db) edge = randEdgePolicy(chanID, op, db)
edge.Flags = 1 edge.ChannelFlags = 1
edge.Node = graphNodes[i] edge.Node = graphNodes[i]
edge.SigBytes = testSig.Serialize() edge.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge); err != nil { if err := graph.UpdateEdgePolicy(edge); err != nil {
@ -1414,7 +1431,7 @@ func TestChanUpdatesInHorizon(t *testing.T) {
edge1 := newEdgePolicy( edge1 := newEdgePolicy(
chanID.ToUint64(), op, db, edge1UpdateTime.Unix(), chanID.ToUint64(), op, db, edge1UpdateTime.Unix(),
) )
edge1.Flags = 0 edge1.ChannelFlags = 0
edge1.Node = node2 edge1.Node = node2
edge1.SigBytes = testSig.Serialize() edge1.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge1); err != nil { if err := graph.UpdateEdgePolicy(edge1); err != nil {
@ -1424,7 +1441,7 @@ func TestChanUpdatesInHorizon(t *testing.T) {
edge2 := newEdgePolicy( edge2 := newEdgePolicy(
chanID.ToUint64(), op, db, edge2UpdateTime.Unix(), chanID.ToUint64(), op, db, edge2UpdateTime.Unix(),
) )
edge2.Flags = 1 edge2.ChannelFlags = 1
edge2.Node = node1 edge2.Node = node1
edge2.SigBytes = testSig.Serialize() edge2.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge2); err != nil { if err := graph.UpdateEdgePolicy(edge2); err != nil {
@ -1915,7 +1932,7 @@ func TestFetchChanInfos(t *testing.T) {
edge1 := newEdgePolicy( edge1 := newEdgePolicy(
chanID.ToUint64(), op, db, updateTime.Unix(), chanID.ToUint64(), op, db, updateTime.Unix(),
) )
edge1.Flags = 0 edge1.ChannelFlags = 0
edge1.Node = node2 edge1.Node = node2
edge1.SigBytes = testSig.Serialize() edge1.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge1); err != nil { if err := graph.UpdateEdgePolicy(edge1); err != nil {
@ -1925,7 +1942,7 @@ func TestFetchChanInfos(t *testing.T) {
edge2 := newEdgePolicy( edge2 := newEdgePolicy(
chanID.ToUint64(), op, db, updateTime.Unix(), chanID.ToUint64(), op, db, updateTime.Unix(),
) )
edge2.Flags = 1 edge2.ChannelFlags = 1
edge2.Node = node1 edge2.Node = node1
edge2.SigBytes = testSig.Serialize() edge2.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge2); err != nil { if err := graph.UpdateEdgePolicy(edge2); err != nil {
@ -2053,7 +2070,7 @@ func TestIncompleteChannelPolicies(t *testing.T) {
edgePolicy := newEdgePolicy( edgePolicy := newEdgePolicy(
chanID.ToUint64(), op, db, updateTime.Unix(), chanID.ToUint64(), op, db, updateTime.Unix(),
) )
edgePolicy.Flags = 0 edgePolicy.ChannelFlags = 0
edgePolicy.Node = node2 edgePolicy.Node = node2
edgePolicy.SigBytes = testSig.Serialize() edgePolicy.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil { if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
@ -2068,7 +2085,7 @@ func TestIncompleteChannelPolicies(t *testing.T) {
edgePolicy = newEdgePolicy( edgePolicy = newEdgePolicy(
chanID.ToUint64(), op, db, updateTime.Unix(), chanID.ToUint64(), op, db, updateTime.Unix(),
) )
edgePolicy.Flags = 1 edgePolicy.ChannelFlags = 1
edgePolicy.Node = node1 edgePolicy.Node = node1
edgePolicy.SigBytes = testSig.Serialize() edgePolicy.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edgePolicy); err != nil { if err := graph.UpdateEdgePolicy(edgePolicy); err != nil {
@ -2125,7 +2142,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) {
} }
edge1 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db) edge1 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db)
edge1.Flags = 0 edge1.ChannelFlags = 0
edge1.Node = node1 edge1.Node = node1
edge1.SigBytes = testSig.Serialize() edge1.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge1); err != nil { if err := graph.UpdateEdgePolicy(edge1); err != nil {
@ -2133,7 +2150,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) {
} }
edge2 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db) edge2 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db)
edge2.Flags = 1 edge2.ChannelFlags = 1
edge2.Node = node2 edge2.Node = node2
edge2.SigBytes = testSig.Serialize() edge2.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge2); err != nil { 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 // Now, we'll update the edge policies to ensure the old timestamps are
// removed from the update index. // removed from the update index.
edge1.Flags = 2 edge1.ChannelFlags = 2
edge1.LastUpdate = time.Now() edge1.LastUpdate = time.Now()
if err := graph.UpdateEdgePolicy(edge1); err != nil { if err := graph.UpdateEdgePolicy(edge1); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
edge2.Flags = 3 edge2.ChannelFlags = 3
edge2.LastUpdate = edge1.LastUpdate.Add(time.Hour) edge2.LastUpdate = edge1.LastUpdate.Add(time.Hour)
if err := graph.UpdateEdgePolicy(edge2); err != nil { if err := graph.UpdateEdgePolicy(edge2); err != nil {
t.Fatalf("unable to update edge: %v", err) 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 // We'll now insert an advertised edge, but it'll only be the edge that
// points from the first to the second node. // points from the first to the second node.
edge1 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db) edge1 := randEdgePolicy(chanID.ToUint64(), edgeInfo.ChannelPoint, db)
edge1.Flags = 0 edge1.ChannelFlags = 0
edge1.Node = node1 edge1.Node = node1
edge1.SigBytes = testSig.Serialize() edge1.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(edge1); err != nil { 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 // compareNodes is used to compare two LightningNodes while excluding the
// Features struct, which cannot be compared as the semantics for reserializing // Features struct, which cannot be compared as the semantics for reserializing
// the featuresMap have not been defined. // 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 "+ return fmt.Errorf("edge LastUpdate doesn't match: expected %#v, \n "+
"got %#v", a.LastUpdate, b.LastUpdate) "got %#v", a.LastUpdate, b.LastUpdate)
} }
if a.Flags != b.Flags { if a.MessageFlags != b.MessageFlags {
return fmt.Errorf("Flags doesn't match: expected %v, "+ return fmt.Errorf("MessageFlags doesn't match: expected %v, "+
"got %v", a.Flags, b.Flags) "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 { if a.TimeLockDelta != b.TimeLockDelta {
return fmt.Errorf("TimeLockDelta doesn't match: expected %v, "+ 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, "+ return fmt.Errorf("MinHTLC doesn't match: expected %v, "+
"got %v", a.MinHTLC, b.MinHTLC) "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 { if a.FeeBaseMSat != b.FeeBaseMSat {
return fmt.Errorf("FeeBaseMSat doesn't match: expected %v, "+ return fmt.Errorf("FeeBaseMSat doesn't match: expected %v, "+
"got %v", a.FeeBaseMSat, b.FeeBaseMSat) "got %v", a.FeeBaseMSat, b.FeeBaseMSat)

@ -327,9 +327,11 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash,
ChainHash: chanInfo.ChainHash, ChainHash: chanInfo.ChainHash,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
Timestamp: uint32(e1.LastUpdate.Unix()), Timestamp: uint32(e1.LastUpdate.Unix()),
Flags: e1.Flags, MessageFlags: e1.MessageFlags,
ChannelFlags: e1.ChannelFlags,
TimeLockDelta: e1.TimeLockDelta, TimeLockDelta: e1.TimeLockDelta,
HtlcMinimumMsat: e1.MinHTLC, HtlcMinimumMsat: e1.MinHTLC,
HtlcMaximumMsat: e1.MaxHTLC,
BaseFee: uint32(e1.FeeBaseMSat), BaseFee: uint32(e1.FeeBaseMSat),
FeeRate: uint32(e1.FeeProportionalMillionths), FeeRate: uint32(e1.FeeProportionalMillionths),
ExtraOpaqueData: e1.ExtraOpaqueData, ExtraOpaqueData: e1.ExtraOpaqueData,
@ -346,9 +348,11 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash,
ChainHash: chanInfo.ChainHash, ChainHash: chanInfo.ChainHash,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
Timestamp: uint32(e2.LastUpdate.Unix()), Timestamp: uint32(e2.LastUpdate.Unix()),
Flags: e2.Flags, MessageFlags: e2.MessageFlags,
ChannelFlags: e2.ChannelFlags,
TimeLockDelta: e2.TimeLockDelta, TimeLockDelta: e2.TimeLockDelta,
HtlcMinimumMsat: e2.MinHTLC, HtlcMinimumMsat: e2.MinHTLC,
HtlcMaximumMsat: e2.MaxHTLC,
BaseFee: uint32(e2.FeeBaseMSat), BaseFee: uint32(e2.FeeBaseMSat),
FeeRate: uint32(e2.FeeProportionalMillionths), FeeRate: uint32(e2.FeeProportionalMillionths),
ExtraOpaqueData: e2.ExtraOpaqueData, ExtraOpaqueData: e2.ExtraOpaqueData,

@ -560,7 +560,7 @@ func (d *AuthenticatedGossiper) ProcessLocalAnnouncement(msg lnwire.Message,
} }
// channelUpdateID is a unique identifier for ChannelUpdate messages, as // 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. // tuple.
type channelUpdateID struct { type channelUpdateID struct {
// channelID represents the set of data which is needed to // 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 // Flags least-significant bit must be set to 0 if the creating node
// corresponds to the first node in the previously sent channel // corresponds to the first node in the previously sent channel
// announcement and 1 otherwise. // announcement and 1 otherwise.
flags lnwire.ChanUpdateFlag flags lnwire.ChanUpdateChanFlags
} }
// msgWithSenders is a wrapper struct around a message, and the set of peers // 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{}{} mws.senders[sender] = struct{}{}
d.channelAnnouncements[deDupKey] = mws d.channelAnnouncements[deDupKey] = mws
// Channel updates are identified by the (short channel id, flags) // Channel updates are identified by the (short channel id,
// tuple. // channelflags) tuple.
case *lnwire.ChannelUpdate: case *lnwire.ChannelUpdate:
sender := routing.NewVertex(message.source) sender := routing.NewVertex(message.source)
deDupKey := channelUpdateID{ deDupKey := channelUpdateID{
msg.ShortChannelID, msg.ShortChannelID,
msg.Flags, msg.ChannelFlags,
} }
oldTimestamp := uint32(0) oldTimestamp := uint32(0)
@ -1321,6 +1321,17 @@ func (d *AuthenticatedGossiper) retransmitStaleChannels() error {
return nil 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 const broadcastInterval = time.Hour * 24
timeElapsed := time.Since(edge.LastUpdate) timeElapsed := time.Since(edge.LastUpdate)
@ -1911,7 +1922,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
// announcement for this edge. // announcement for this edge.
timestamp := time.Unix(int64(msg.Timestamp), 0) timestamp := time.Unix(int64(msg.Timestamp), 0)
if d.cfg.Router.IsStaleEdgePolicy( if d.cfg.Router.IsStaleEdgePolicy(
msg.ShortChannelID, timestamp, msg.Flags, msg.ShortChannelID, timestamp, msg.ChannelFlags,
) { ) {
nMsg.err <- nil nMsg.err <- nil
@ -1986,16 +1997,17 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
// edge is being updated. // edge is being updated.
var pubKey *btcec.PublicKey var pubKey *btcec.PublicKey
switch { switch {
case msg.Flags&lnwire.ChanUpdateDirection == 0: case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0:
pubKey, _ = chanInfo.NodeKey1() pubKey, _ = chanInfo.NodeKey1()
case msg.Flags&lnwire.ChanUpdateDirection == 1: case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1:
pubKey, _ = chanInfo.NodeKey2() pubKey, _ = chanInfo.NodeKey2()
} }
// Validate the channel announcement with the expected public // Validate the channel announcement with the expected public key and
// key, In the case of an invalid channel , we'll return an // channel capacity. In the case of an invalid channel update, we'll
// error to the caller and exit early. // return an error to the caller and exit early.
if err := routing.ValidateChannelUpdateAnn(pubKey, msg); err != nil { err = routing.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, msg)
if err != nil {
rErr := fmt.Errorf("unable to validate channel "+ rErr := fmt.Errorf("unable to validate channel "+
"update announcement for short_chan_id=%v: %v", "update announcement for short_chan_id=%v: %v",
spew.Sdump(msg.ShortChannelID), err) spew.Sdump(msg.ShortChannelID), err)
@ -2009,9 +2021,11 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
SigBytes: msg.Signature.ToSignatureBytes(), SigBytes: msg.Signature.ToSignatureBytes(),
ChannelID: shortChanID, ChannelID: shortChanID,
LastUpdate: timestamp, LastUpdate: timestamp,
Flags: msg.Flags, MessageFlags: msg.MessageFlags,
ChannelFlags: msg.ChannelFlags,
TimeLockDelta: msg.TimeLockDelta, TimeLockDelta: msg.TimeLockDelta,
MinHTLC: msg.HtlcMinimumMsat, MinHTLC: msg.HtlcMinimumMsat,
MaxHTLC: msg.HtlcMaximumMsat,
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
ExtraOpaqueData: msg.ExtraOpaqueData, ExtraOpaqueData: msg.ExtraOpaqueData,
@ -2041,9 +2055,9 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
// Get our peer's public key. // Get our peer's public key.
var remotePub *btcec.PublicKey var remotePub *btcec.PublicKey
switch { switch {
case msg.Flags&lnwire.ChanUpdateDirection == 0: case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0:
remotePub, _ = chanInfo.NodeKey2() remotePub, _ = chanInfo.NodeKey2()
case msg.Flags&lnwire.ChanUpdateDirection == 1: case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1:
remotePub, _ = chanInfo.NodeKey1() remotePub, _ = chanInfo.NodeKey1()
} }
@ -2504,7 +2518,12 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
*lnwire.ChannelUpdate, error) { *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 // Make sure timestamp is always increased, such that our update gets
// propagated. // propagated.
@ -2513,17 +2532,22 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
timestamp = edge.LastUpdate.Unix() + 1 timestamp = edge.LastUpdate.Unix() + 1
} }
edge.LastUpdate = time.Unix(timestamp, 0) edge.LastUpdate = time.Unix(timestamp, 0)
chanUpdate := &lnwire.ChannelUpdate{ chanUpdate := &lnwire.ChannelUpdate{
ChainHash: info.ChainHash, ChainHash: info.ChainHash,
ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID), ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID),
Timestamp: uint32(timestamp), Timestamp: uint32(timestamp),
Flags: edge.Flags, MessageFlags: edge.MessageFlags,
ChannelFlags: edge.ChannelFlags,
TimeLockDelta: edge.TimeLockDelta, TimeLockDelta: edge.TimeLockDelta,
HtlcMinimumMsat: edge.MinHTLC, HtlcMinimumMsat: edge.MinHTLC,
HtlcMaximumMsat: edge.MaxHTLC,
BaseFee: uint32(edge.FeeBaseMSat), BaseFee: uint32(edge.FeeBaseMSat),
FeeRate: uint32(edge.FeeProportionalMillionths), FeeRate: uint32(edge.FeeProportionalMillionths),
ExtraOpaqueData: edge.ExtraOpaqueData, ExtraOpaqueData: edge.ExtraOpaqueData,
} }
var err error
chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(edge.SigBytes) chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(edge.SigBytes)
if err != nil { if err != nil {
return nil, nil, err 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 // To ensure that our signature is valid, we'll verify it ourself
// before committing it to the slice returned. // before committing it to the slice returned.
err = routing.ValidateChannelUpdateAnn(d.selfKey, chanUpdate) err = routing.ValidateChannelUpdateAnn(d.selfKey, info.Capacity, chanUpdate)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("generated invalid channel "+ return nil, nil, fmt.Errorf("generated invalid channel "+
"update sig: %v", err) "update sig: %v", err)

@ -10,6 +10,7 @@ import (
"net" "net"
"os" "os"
"reflect" "reflect"
"strings"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -17,6 +18,7 @@ import (
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
@ -54,9 +56,10 @@ var (
nodeKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256()) nodeKeyPriv2, _ = btcec.NewPrivateKey(btcec.S256())
nodeKeyPub2 = nodeKeyPriv2.PubKey() nodeKeyPub2 = nodeKeyPriv2.PubKey()
trickleDelay = time.Millisecond * 100 trickleDelay = time.Millisecond * 100
retransmitDelay = time.Hour * 1 retransmitDelay = time.Hour * 1
proofMatureDelta uint32 proofMatureDelta uint32
maxBtcFundingAmount = btcutil.Amount(1<<62) - 1
) )
// makeTestDB creates a new instance of the ChannelDB for testing purposes. A // 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 { if _, ok := r.infos[info.ChannelID]; ok {
return errors.New("info already exist") 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 r.infos[info.ChannelID] = info
return nil 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 // IsStaleEdgePolicy returns true if the graph source has a channel edge for
// the passed channel ID (and flags) that have a more recent timestamp. // the passed channel ID (and flags) that have a more recent timestamp.
func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID, 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()] edges, ok := r.edges[chanID.ToUint64()]
if !ok { if !ok {
@ -267,10 +274,10 @@ func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
switch { switch {
case len(edges) >= 1 && edges[0].Flags == flags: case len(edges) >= 1 && edges[0].ChannelFlags == flags:
return !edges[0].LastUpdate.Before(timestamp) 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) return !edges[1].LastUpdate.Before(timestamp)
default: default:
@ -440,20 +447,27 @@ func createNodeAnnouncement(priv *btcec.PrivateKey,
return a, nil return a, nil
} }
func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag, func createUpdateAnnouncement(blockHeight uint32,
flags lnwire.ChanUpdateChanFlags,
nodeKey *btcec.PrivateKey, timestamp uint32, nodeKey *btcec.PrivateKey, timestamp uint32,
extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) { extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) {
var err error var err error
htlcMinMsat := lnwire.MilliSatoshi(prand.Int63())
a := &lnwire.ChannelUpdate{ a := &lnwire.ChannelUpdate{
ShortChannelID: lnwire.ShortChannelID{ ShortChannelID: lnwire.ShortChannelID{
BlockHeight: blockHeight, BlockHeight: blockHeight,
}, },
Timestamp: timestamp, Timestamp: timestamp,
MessageFlags: lnwire.ChanUpdateOptionMaxHtlc,
ChannelFlags: flags,
TimeLockDelta: uint16(prand.Int63()), TimeLockDelta: uint16(prand.Int63()),
Flags: flags, HtlcMinimumMsat: htlcMinMsat,
HtlcMinimumMsat: lnwire.MilliSatoshi(prand.Int63()),
// 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()), FeeRate: uint32(prand.Int31()),
BaseFee: uint32(prand.Int31()), BaseFee: uint32(prand.Int31()),
} }
@ -461,14 +475,7 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag,
a.ExtraOpaqueData = extraBytes[0] a.ExtraOpaqueData = extraBytes[0]
} }
pub := nodeKey.PubKey() err = signUpdate(nodeKey, a)
signer := mockSigner{nodeKey}
sig, err := SignAnnouncement(&signer, pub, a)
if err != nil {
return nil, err
}
a.Signature, err = lnwire.NewSigFromSignature(sig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -476,6 +483,22 @@ func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateFlag,
return a, nil 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, func createAnnouncementWithoutProof(blockHeight uint32,
extraBytes ...[]byte) *lnwire.ChannelAnnouncement { extraBytes ...[]byte) *lnwire.ChannelAnnouncement {
@ -2028,7 +2051,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) { assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) {
channelKey := channelUpdateID{ channelKey := channelUpdateID{
ua3.ShortChannelID, ua3.ShortChannelID,
ua3.Flags, ua3.ChannelFlags,
} }
mws, ok := announcements.channelUpdates[channelKey] 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 // mockPeer implements the lnpeer.Peer interface and is used to test the
// gossiper's interaction with peers. // gossiper's interaction with peers.
type mockPeer struct { type mockPeer struct {

@ -72,9 +72,11 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
ChainHash: chanInfo.ChainHash, ChainHash: chanInfo.ChainHash,
ShortChannelID: chanID, ShortChannelID: chanID,
Timestamp: uint32(e1.LastUpdate.Unix()), Timestamp: uint32(e1.LastUpdate.Unix()),
Flags: e1.Flags, MessageFlags: e1.MessageFlags,
ChannelFlags: e1.ChannelFlags,
TimeLockDelta: e1.TimeLockDelta, TimeLockDelta: e1.TimeLockDelta,
HtlcMinimumMsat: e1.MinHTLC, HtlcMinimumMsat: e1.MinHTLC,
HtlcMaximumMsat: e1.MaxHTLC,
BaseFee: uint32(e1.FeeBaseMSat), BaseFee: uint32(e1.FeeBaseMSat),
FeeRate: uint32(e1.FeeProportionalMillionths), FeeRate: uint32(e1.FeeProportionalMillionths),
ExtraOpaqueData: e1.ExtraOpaqueData, ExtraOpaqueData: e1.ExtraOpaqueData,
@ -89,9 +91,11 @@ func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof,
ChainHash: chanInfo.ChainHash, ChainHash: chanInfo.ChainHash,
ShortChannelID: chanID, ShortChannelID: chanID,
Timestamp: uint32(e2.LastUpdate.Unix()), Timestamp: uint32(e2.LastUpdate.Unix()),
Flags: e2.Flags, MessageFlags: e2.MessageFlags,
ChannelFlags: e2.ChannelFlags,
TimeLockDelta: e2.TimeLockDelta, TimeLockDelta: e2.TimeLockDelta,
HtlcMinimumMsat: e2.MinHTLC, HtlcMinimumMsat: e2.MinHTLC,
HtlcMaximumMsat: e2.MaxHTLC,
BaseFee: uint32(e2.FeeBaseMSat), BaseFee: uint32(e2.FeeBaseMSat),
FeeRate: uint32(e2.FeeProportionalMillionths), FeeRate: uint32(e2.FeeProportionalMillionths),
ExtraOpaqueData: e2.ExtraOpaqueData, ExtraOpaqueData: e2.ExtraOpaqueData,

@ -1090,7 +1090,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs, MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs,
CsvDelay: msg.CsvDelay, CsvDelay: msg.CsvDelay,
} }
err = reservation.CommitConstraints(channelConstraints) err = reservation.CommitConstraints(channelConstraints, amt)
if err != nil { if err != nil {
fndgLog.Errorf("Unacceptable channel constraints: %v", err) fndgLog.Errorf("Unacceptable channel constraints: %v", err)
f.failFundingFlow(fmsg.peer, fmsg.msg.PendingChannelID, err) f.failFundingFlow(fmsg.peer, fmsg.msg.PendingChannelID, err)
@ -1254,7 +1254,9 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs, MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs,
CsvDelay: msg.CsvDelay, CsvDelay: msg.CsvDelay,
} }
err = resCtx.reservation.CommitConstraints(channelConstraints) err = resCtx.reservation.CommitConstraints(
channelConstraints, resCtx.chanAmt,
)
if err != nil { if err != nil {
fndgLog.Warnf("Unacceptable channel constraints: %v", err) fndgLog.Warnf("Unacceptable channel constraints: %v", err)
f.failFundingFlow(fmsg.peer, fmsg.msg.PendingChannelID, 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. // need to determine the smallest HTLC it deems economically relevant.
fwdMinHTLC := completeChan.LocalChanCfg.MinHTLC 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( ann, err := f.newChanAnnouncement(
f.cfg.IDKey, completeChan.IdentityPub, f.cfg.IDKey, completeChan.IdentityPub,
completeChan.LocalChanCfg.MultiSigKey.PubKey, completeChan.LocalChanCfg.MultiSigKey.PubKey,
completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID,
chanID, fwdMinHTLC, chanID, fwdMinHTLC, fwdMaxHTLC,
) )
if err != nil { if err != nil {
return fmt.Errorf("error generating channel "+ return fmt.Errorf("error generating channel "+
@ -2278,13 +2287,20 @@ func (f *fundingManager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
// HTLC it deems economically relevant. // HTLC it deems economically relevant.
fwdMinHTLC := completeChan.LocalChanCfg.MinHTLC 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 // Create and broadcast the proofs required to make this channel
// public and usable for other nodes for routing. // public and usable for other nodes for routing.
err = f.announceChannel( err = f.announceChannel(
f.cfg.IDKey, completeChan.IdentityPub, f.cfg.IDKey, completeChan.IdentityPub,
completeChan.LocalChanCfg.MultiSigKey.PubKey, completeChan.LocalChanCfg.MultiSigKey.PubKey,
completeChan.RemoteChanCfg.MultiSigKey.PubKey, completeChan.RemoteChanCfg.MultiSigKey.PubKey,
*shortChanID, chanID, fwdMinHTLC, *shortChanID, chanID, fwdMinHTLC, fwdMaxHTLC,
) )
if err != nil { if err != nil {
return fmt.Errorf("channel announcement failed: %v", err) return fmt.Errorf("channel announcement failed: %v", err)
@ -2451,7 +2467,7 @@ type chanAnnouncement struct {
func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey, func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey,
localFundingKey, remoteFundingKey *btcec.PublicKey, localFundingKey, remoteFundingKey *btcec.PublicKey,
shortChanID lnwire.ShortChannelID, chanID lnwire.ChannelID, shortChanID lnwire.ShortChannelID, chanID lnwire.ChannelID,
fwdMinHTLC lnwire.MilliSatoshi) (*chanAnnouncement, error) { fwdMinHTLC, fwdMaxHTLC lnwire.MilliSatoshi) (*chanAnnouncement, error) {
chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash chainHash := *f.cfg.Wallet.Cfg.NetParams.GenesisHash
@ -2468,7 +2484,7 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey,
// being updated within the ChannelUpdateAnnouncement announcement // being updated within the ChannelUpdateAnnouncement announcement
// below. A value of zero means it's the edge of the "first" node and 1 // below. A value of zero means it's the edge of the "first" node and 1
// being the other node. // being the other node.
var chanFlags lnwire.ChanUpdateFlag var chanFlags lnwire.ChanUpdateChanFlags
// The lexicographical ordering of the two identity public keys of the // The lexicographical ordering of the two identity public keys of the
// nodes indicates which of the nodes is "first". If our serialized // nodes indicates which of the nodes is "first". If our serialized
@ -2496,19 +2512,25 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey,
chanFlags = 1 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 // We announce the channel with the default values. Some of
// these values can later be changed by crafting a new ChannelUpdate. // these values can later be changed by crafting a new ChannelUpdate.
chanUpdateAnn := &lnwire.ChannelUpdate{ chanUpdateAnn := &lnwire.ChannelUpdate{
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChainHash: chainHash, ChainHash: chainHash,
Timestamp: uint32(time.Now().Unix()), Timestamp: uint32(time.Now().Unix()),
Flags: chanFlags, MessageFlags: msgFlags,
ChannelFlags: chanFlags,
TimeLockDelta: uint16(f.cfg.DefaultRoutingPolicy.TimeLockDelta), TimeLockDelta: uint16(f.cfg.DefaultRoutingPolicy.TimeLockDelta),
// We use the HtlcMinimumMsat that the remote party required us // We use the HtlcMinimumMsat that the remote party required us
// to use, as our ChannelUpdate will be used to carry HTLCs // to use, as our ChannelUpdate will be used to carry HTLCs
// towards them. // towards them.
HtlcMinimumMsat: fwdMinHTLC, HtlcMinimumMsat: fwdMinHTLC,
HtlcMaximumMsat: fwdMaxHTLC,
BaseFee: uint32(f.cfg.DefaultRoutingPolicy.BaseFee), BaseFee: uint32(f.cfg.DefaultRoutingPolicy.BaseFee),
FeeRate: uint32(f.cfg.DefaultRoutingPolicy.FeeRate), FeeRate: uint32(f.cfg.DefaultRoutingPolicy.FeeRate),
@ -2587,7 +2609,7 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey,
// finish, either successfully or with an error. // finish, either successfully or with an error.
func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKey, func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKey,
remoteFundingKey *btcec.PublicKey, shortChanID lnwire.ShortChannelID, 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 // First, we'll create the batch of announcements to be sent upon
// initial channel creation. This includes the channel announcement // 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. // proof needed to fully authenticate the channel.
ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey, ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey,
localFundingKey, remoteFundingKey, shortChanID, chanID, localFundingKey, remoteFundingKey, shortChanID, chanID,
fwdMinHTLC, fwdMinHTLC, fwdMaxHTLC,
) )
if err != nil { if err != nil {
fndgLog.Errorf("can't generate channel announcement: %v", err) fndgLog.Errorf("can't generate channel announcement: %v", err)

@ -433,6 +433,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
FeeRate: 1000, FeeRate: 1000,
TimeLockDelta: 10, TimeLockDelta: 10,
}, },
RequiredRemoteMaxValue: oldCfg.RequiredRemoteMaxValue,
PublishTransaction: func(txn *wire.MsgTx) error { PublishTransaction: func(txn *wire.MsgTx) error {
publishChan <- txn publishChan <- txn
return nil 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 // advertised value will be checked against the other node's default min_htlc
// value. // value.
func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, 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 // After the FundingLocked message is sent, Alice and Bob will each
// send the following messages to their gossiper: // send the following messages to their gossiper:
@ -871,6 +873,24 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode,
minHtlc, m.HtlcMinimumMsat) 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 gotChannelUpdate = true
} }
} }
@ -1005,8 +1025,11 @@ func TestFundingManagerNormalWorkflow(t *testing.T) {
// Run through the process of opening the channel, up until the funding // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, localAmt := btcutil.Amount(500000)
true) 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. // Check that neither Alice nor Bob sent an error message.
assertErrorNotSent(t, alice.msgChan) assertErrorNotSent(t, alice.msgChan)
@ -1037,7 +1060,7 @@ func TestFundingManagerNormalWorkflow(t *testing.T) {
// Make sure both fundingManagers send the expected channel // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) 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 // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
localAmt := btcutil.Amount(500000)
pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
updateChan := make(chan *lnrpc.OpenStatusUpdate) updateChan := make(chan *lnrpc.OpenStatusUpdate)
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
true) updateChan, true)
// After the funding transaction gets mined, both nodes will send the // After the funding transaction gets mined, both nodes will send the
// fundingLocked message to the other peer. If the funding node fails // 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 // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) 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 // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
localAmt := btcutil.Amount(500000)
pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
updateChan := make(chan *lnrpc.OpenStatusUpdate) updateChan := make(chan *lnrpc.OpenStatusUpdate)
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
true) updateChan, true)
// After the funding transaction gets mined, both nodes will send the // After the funding transaction gets mined, both nodes will send the
// fundingLocked message to the other peer. If the funding node fails // 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 // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) 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 // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, localAmt := btcutil.Amount(500000)
true) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
updateChan, true)
// Notify that transaction was mined // Notify that transaction was mined
alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{}
@ -1684,7 +1716,7 @@ func TestFundingManagerReceiveFundingLockedTwice(t *testing.T) {
// Make sure both fundingManagers send the expected channel // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) 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 // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, localAmt := btcutil.Amount(500000)
true) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
updateChan, true)
// Notify that transaction was mined // Notify that transaction was mined
alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{}
@ -1773,7 +1808,7 @@ func TestFundingManagerRestartAfterChanAnn(t *testing.T) {
// Make sure both fundingManagers send the expected channel // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) 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 // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, localAmt := btcutil.Amount(500000)
true) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
updateChan, true)
// Notify that transaction was mined // Notify that transaction was mined
alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{}
@ -1860,7 +1898,7 @@ func TestFundingManagerRestartAfterReceivingFundingLocked(t *testing.T) {
// Make sure both fundingManagers send the expected channel // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertAddedToRouterGraph(t, alice, bob, fundingOutPoint) 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 // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, localAmt := btcutil.Amount(500000)
false) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
updateChan, false)
// Notify that transaction was mined // Notify that transaction was mined
alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{}
@ -1917,7 +1958,7 @@ func TestFundingManagerPrivateChannel(t *testing.T) {
// Make sure both fundingManagers send the expected channel // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// The funding transaction is now confirmed, wait for the // The funding transaction is now confirmed, wait for the
// OpenStatusUpdate_ChanOpen update // OpenStatusUpdate_ChanOpen update
@ -1988,8 +2029,11 @@ func TestFundingManagerPrivateRestart(t *testing.T) {
// Run through the process of opening the channel, up until the funding // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan, localAmt := btcutil.Amount(500000)
false) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt
fundingOutPoint := openChannel(t, alice, bob, localAmt, pushAmt, 1,
updateChan, false)
// Notify that transaction was mined // Notify that transaction was mined
alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{}
@ -2016,7 +2060,7 @@ func TestFundingManagerPrivateRestart(t *testing.T) {
// Make sure both fundingManagers send the expected channel // Make sure both fundingManagers send the expected channel
// announcements. // announcements.
assertChannelAnnouncements(t, alice, bob) assertChannelAnnouncements(t, alice, bob, capacity)
// Note: We don't check for the addedToRouterGraph state because in // Note: We don't check for the addedToRouterGraph state because in
// the private channel mode, the state is quickly changed from // the private channel mode, the state is quickly changed from
@ -2110,14 +2154,18 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) {
// needed. // needed.
updateChan := make(chan *lnrpc.OpenStatusUpdate) 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 // Create a funding request with the custom parameters and start the
// workflow. // workflow.
errChan := make(chan error, 1) errChan := make(chan error, 1)
initReq := &openChanReq{ initReq := &openChanReq{
targetPubkey: bob.privKey.PubKey(), targetPubkey: bob.privKey.PubKey(),
chainHash: *activeNetParams.GenesisHash, chainHash: *activeNetParams.GenesisHash,
localFundingAmt: 5000000, localFundingAmt: localAmt,
pushAmt: lnwire.NewMSatFromSatoshis(0), pushAmt: lnwire.NewMSatFromSatoshis(pushAmt),
private: false, private: false,
minHtlc: minHtlc, minHtlc: minHtlc,
remoteCsvDelay: csvDelay, remoteCsvDelay: csvDelay,
@ -2313,7 +2361,7 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) {
// announcements. Alice should advertise the default MinHTLC value of // announcements. Alice should advertise the default MinHTLC value of
// 5, while bob should advertise the value minHtlc, since Alice // 5, while bob should advertise the value minHtlc, since Alice
// required him to use it. // 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 // The funding transaction is now confirmed, wait for the
// OpenStatusUpdate_ChanOpen update // OpenStatusUpdate_ChanOpen update
@ -2609,3 +2657,69 @@ func TestFundingManagerMaxConfs(t *testing.T) {
string(err.Data)) 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))
}
}

@ -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 // ErrChanTooSmall returns an error indicating that an incoming channel request
// was too small. We'll reject any incoming channels if they're below our // was too small. We'll reject any incoming channels if they're below our
// configured value for the min channel size we'll accept. // configured value for the min channel size we'll accept.

@ -437,7 +437,9 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
CsvDelay: csvDelay, CsvDelay: csvDelay,
} }
err = aliceChanReservation.CommitConstraints(channelConstraints) err = aliceChanReservation.CommitConstraints(
channelConstraints, fundingAmount*2,
)
if err != nil { if err != nil {
t.Fatalf("unable to verify constraints: %v", err) t.Fatalf("unable to verify constraints: %v", err)
} }
@ -471,7 +473,9 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
if err != nil { if err != nil {
t.Fatalf("bob unable to init channel reservation: %v", err) t.Fatalf("bob unable to init channel reservation: %v", err)
} }
err = bobChanReservation.CommitConstraints(channelConstraints) err = bobChanReservation.CommitConstraints(
channelConstraints, fundingAmount*2,
)
if err != nil { if err != nil {
t.Fatalf("unable to verify constraints: %v", err) t.Fatalf("unable to verify constraints: %v", err)
} }
@ -869,7 +873,9 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
CsvDelay: csvDelay, CsvDelay: csvDelay,
} }
err = aliceChanReservation.CommitConstraints(channelConstraints) err = aliceChanReservation.CommitConstraints(
channelConstraints, fundingAmt,
)
if err != nil { if err != nil {
t.Fatalf("unable to verify constraints: %v", err) t.Fatalf("unable to verify constraints: %v", err)
} }
@ -903,7 +909,9 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
if err != nil { if err != nil {
t.Fatalf("unable to create bob reservation: %v", err) t.Fatalf("unable to create bob reservation: %v", err)
} }
err = bobChanReservation.CommitConstraints(channelConstraints) err = bobChanReservation.CommitConstraints(
channelConstraints, fundingAmt,
)
if err != nil { if err != nil {
t.Fatalf("unable to verify constraints: %v", err) t.Fatalf("unable to verify constraints: %v", err)
} }

@ -286,7 +286,9 @@ func (r *ChannelReservation) SetNumConfsRequired(numConfs uint16) {
// of satoshis that can be transferred in a single commitment. This function // of satoshis that can be transferred in a single commitment. This function
// will also attempt to verify the constraints for sanity, returning an error // will also attempt to verify the constraints for sanity, returning an error
// if the parameters are seemed unsound. // 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() r.Lock()
defer r.Unlock() 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. // channel reserve.
if r.ourContribution.DustLimit > c.ChanReserve { if r.ourContribution.DustLimit > c.ChanReserve {
r.ourContribution.DustLimit = c.ChanReserve r.ourContribution.DustLimit = c.ChanReserve

@ -2,22 +2,44 @@ package lnwire
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "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 // particular channel edge. Each bit is to be examined in order to determine
// how the ChannelUpdate message is to be interpreted. // how the ChannelUpdate message is to be interpreted.
type ChanUpdateFlag uint16 type ChanUpdateChanFlags uint8
const ( const (
// ChanUpdateDirection indicates the direction of a channel update. If // ChanUpdateDirection indicates the direction of a channel update. If
// this bit is set to 0 if Node1 (the node with the "smaller" Node ID) // this bit is set to 0 if Node1 (the node with the "smaller" Node ID)
// is updating the channel, and to 1 otherwise. // 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 // ChanUpdateDisabled is a bit that indicates if the channel edge
// selected by the ChanUpdateDirection bit is to be treated as being // selected by the ChanUpdateDirection bit is to be treated as being
@ -25,6 +47,11 @@ const (
ChanUpdateDisabled 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. // ChannelUpdate message is used after channel has been initially announced.
// Each side independently announces its fees and minimum expiry for HTLCs and // Each side independently announces its fees and minimum expiry for HTLCs and
// other parameters. Also this message is used to redeclare initially set // other parameters. Also this message is used to redeclare initially set
@ -48,13 +75,18 @@ type ChannelUpdate struct {
// the last-received. // the last-received.
Timestamp uint32 Timestamp uint32
// Flags is a bitfield that describes additional meta-data concerning // MessageFlags is a bitfield that describes whether optional fields
// how the update is to be interpreted. Currently, the // 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 // least-significant bit must be set to 0 if the creating node
// corresponds to the first node in the previously sent channel // corresponds to the first node in the previously sent channel
// announcement and 1 otherwise. If the second bit is set, then the // announcement and 1 otherwise. If the second bit is set, then the
// channel is set to be disabled. // channel is set to be disabled.
Flags ChanUpdateFlag ChannelFlags ChanUpdateChanFlags
// TimeLockDelta is the minimum number of blocks this node requires to // TimeLockDelta is the minimum number of blocks this node requires to
// be added to the expiry of HTLCs. This is a security parameter // be added to the expiry of HTLCs. This is a security parameter
@ -75,6 +107,9 @@ type ChannelUpdate struct {
// satoshi. // satoshi.
FeeRate uint32 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 // ExtraOpaqueData is the set of data that was appended to this
// message, some of which we may not actually know how to iterate or // 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 // 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.ChainHash[:],
&a.ShortChannelID, &a.ShortChannelID,
&a.Timestamp, &a.Timestamp,
&a.Flags, &a.MessageFlags,
&a.ChannelFlags,
&a.TimeLockDelta, &a.TimeLockDelta,
&a.HtlcMinimumMsat, &a.HtlcMinimumMsat,
&a.BaseFee, &a.BaseFee,
@ -108,6 +144,13 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error {
return err 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, // 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 // 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 // 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. // This is part of the lnwire.Message interface.
func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error { func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error {
return WriteElements(w, err := WriteElements(w,
a.Signature, a.Signature,
a.ChainHash[:], a.ChainHash[:],
a.ShortChannelID, a.ShortChannelID,
a.Timestamp, a.Timestamp,
a.Flags, a.MessageFlags,
a.ChannelFlags,
a.TimeLockDelta, a.TimeLockDelta,
a.HtlcMinimumMsat, a.HtlcMinimumMsat,
a.BaseFee, a.BaseFee,
a.FeeRate, 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 // MsgType returns the integer uniquely identifying this message type on the
@ -168,16 +225,29 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) {
a.ChainHash[:], a.ChainHash[:],
a.ShortChannelID, a.ShortChannelID,
a.Timestamp, a.Timestamp,
a.Flags, a.MessageFlags,
a.ChannelFlags,
a.TimeLockDelta, a.TimeLockDelta,
a.HtlcMinimumMsat, a.HtlcMinimumMsat,
a.BaseFee, a.BaseFee,
a.FeeRate, a.FeeRate,
a.ExtraOpaqueData,
) )
if err != nil { if err != nil {
return nil, err 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 return w.Bytes(), nil
} }

@ -105,9 +105,15 @@ func WriteElement(w io.Writer, element interface{}) error {
if _, err := w.Write(b[:]); err != nil { if _, err := w.Write(b[:]); err != nil {
return err return err
} }
case ChanUpdateFlag: case ChanUpdateMsgFlags:
var b [2]byte var b [1]byte
binary.BigEndian.PutUint16(b[:], uint16(e)) 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 { if _, err := w.Write(b[:]); err != nil {
return err return err
} }
@ -470,12 +476,18 @@ func ReadElement(r io.Reader, element interface{}) error {
return err return err
} }
*e = binary.BigEndian.Uint16(b[:]) *e = binary.BigEndian.Uint16(b[:])
case *ChanUpdateFlag: case *ChanUpdateMsgFlags:
var b [2]byte var b [1]uint8
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := r.Read(b[:]); err != nil {
return err 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: case *ErrorCode:
var b [2]byte var b [2]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, b[:]); err != nil {

@ -178,6 +178,50 @@ func randAddrs(r *rand.Rand) ([]net.Addr, error) {
return []net.Addr{tcp4Addr, tcp6Addr, v2OnionAddr, v3OnionAddr}, nil 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) { func TestMaxOutPointIndex(t *testing.T) {
t.Parallel() t.Parallel()
@ -605,12 +649,26 @@ func TestLightningWireProtocol(t *testing.T) {
}, },
MsgChannelUpdate: func(v []reflect.Value, r *rand.Rand) { MsgChannelUpdate: func(v []reflect.Value, r *rand.Rand) {
var err error 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{ req := ChannelUpdate{
ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())),
Timestamp: uint32(r.Int31()), Timestamp: uint32(r.Int31()),
Flags: ChanUpdateFlag(r.Int31()), MessageFlags: msgFlags,
ChannelFlags: ChanUpdateChanFlags(r.Int31()),
TimeLockDelta: uint16(r.Int31()), TimeLockDelta: uint16(r.Int31()),
HtlcMinimumMsat: MilliSatoshi(r.Int63()), HtlcMinimumMsat: MilliSatoshi(r.Int63()),
HtlcMaximumMsat: maxHtlc,
BaseFee: uint32(r.Int31()), BaseFee: uint32(r.Int31()),
FeeRate: uint32(r.Int31()), FeeRate: uint32(r.Int31()),
} }

@ -20,7 +20,8 @@ var (
Signature: sig, Signature: sig,
ShortChannelID: NewShortChanIDFromInt(1), ShortChannelID: NewShortChanIDFromInt(1),
Timestamp: 1, Timestamp: 1,
Flags: 1, MessageFlags: 0,
ChannelFlags: 1,
} }
) )

@ -1235,10 +1235,10 @@ func messageSummary(msg lnwire.Message) string {
msg.ChainHash, msg.ShortChannelID.ToUint64()) msg.ChainHash, msg.ShortChannelID.ToUint64())
case *lnwire.ChannelUpdate: case *lnwire.ChannelUpdate:
return fmt.Sprintf("chain_hash=%v, short_chan_id=%v, flag=%v, "+ return fmt.Sprintf("chain_hash=%v, short_chan_id=%v, "+
"update_time=%v", msg.ChainHash, "mflags=%v, cflags=%v, update_time=%v", msg.ChainHash,
msg.ShortChannelID.ToUint64(), msg.Flags, msg.ShortChannelID.ToUint64(), msg.MessageFlags,
time.Unix(int64(msg.Timestamp), 0)) msg.ChannelFlags, time.Unix(int64(msg.Timestamp), 0))
case *lnwire.NodeAnnouncement: case *lnwire.NodeAnnouncement:
return fmt.Sprintf("node=%x, update_time=%v", return fmt.Sprintf("node=%x, update_time=%v",

@ -5,6 +5,7 @@ import (
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcutil"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -121,11 +122,16 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error {
} }
// ValidateChannelUpdateAnn validates the channel update announcement by // ValidateChannelUpdateAnn validates the channel update announcement by
// checking that the included signature covers he announcement and has been // checking (1) that the included signature covers the announcement and has been
// signed by the node's private key. // signed by the node's private key, and (2) that the announcement's message
func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, // flags and optional fields are sane.
func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount,
a *lnwire.ChannelUpdate) error { a *lnwire.ChannelUpdate) error {
if err := validateOptionalFields(capacity, a); err != nil {
return err
}
data, err := a.DataToSign() data, err := a.DataToSign()
if err != nil { if err != nil {
return errors.Errorf("unable to reconstruct message: %v", err) return errors.Errorf("unable to reconstruct message: %v", err)
@ -144,3 +150,25 @@ func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey,
return nil 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
}

@ -339,7 +339,7 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange,
// the second node. // the second node.
sourceNode := edgeInfo.NodeKey1 sourceNode := edgeInfo.NodeKey1
connectingNode := edgeInfo.NodeKey2 connectingNode := edgeInfo.NodeKey2
if m.Flags&lnwire.ChanUpdateDirection == 1 { if m.ChannelFlags&lnwire.ChanUpdateDirection == 1 {
sourceNode = edgeInfo.NodeKey2 sourceNode = edgeInfo.NodeKey2
connectingNode = edgeInfo.NodeKey1 connectingNode = edgeInfo.NodeKey1
} }
@ -363,7 +363,7 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange,
FeeRate: m.FeeProportionalMillionths, FeeRate: m.FeeProportionalMillionths,
AdvertisingNode: aNode, AdvertisingNode: aNode,
ConnectingNode: cNode, ConnectingNode: cNode,
Disabled: m.Flags&lnwire.ChanUpdateDisabled != 0, Disabled: m.ChannelFlags&lnwire.ChanUpdateDisabled != 0,
} }
edgeUpdate.AdvertisingNode.Curve = nil edgeUpdate.AdvertisingNode.Curve = nil
edgeUpdate.ConnectingNode.Curve = nil edgeUpdate.ConnectingNode.Curve = nil

@ -402,9 +402,9 @@ func TestEdgeUpdateNotification(t *testing.T) {
// Create random policy edges that are stemmed to the channel id // Create random policy edges that are stemmed to the channel id
// created above. // created above.
edge1 := randEdgePolicy(chanID, node1) edge1 := randEdgePolicy(chanID, node1)
edge1.Flags = 0 edge1.ChannelFlags = 0
edge2 := randEdgePolicy(chanID, node2) edge2 := randEdgePolicy(chanID, node2)
edge2.Flags = 1 edge2.ChannelFlags = 1
if err := ctx.router.UpdateEdge(edge1); err != nil { if err := ctx.router.UpdateEdge(edge1); err != nil {
t.Fatalf("unable to add edge update: %v", err) t.Fatalf("unable to add edge update: %v", err)

@ -562,7 +562,7 @@ func findPath(g *graphParams, r *restrictParams,
// TODO(halseth): also ignore disable flags for non-local // TODO(halseth): also ignore disable flags for non-local
// channels if bandwidth hint is set? // channels if bandwidth hint is set?
isSourceChan := fromVertex == sourceVertex isSourceChan := fromVertex == sourceVertex
edgeFlags := lnwire.ChanUpdateFlag(edge.Flags) edgeFlags := edge.ChannelFlags
isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0 isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0
if !isSourceChan && isDisabled { if !isSourceChan && isDisabled {

@ -271,7 +271,8 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
edgePolicy := &channeldb.ChannelEdgePolicy{ edgePolicy := &channeldb.ChannelEdgePolicy{
SigBytes: testSig.Serialize(), SigBytes: testSig.Serialize(),
Flags: lnwire.ChanUpdateFlag(edge.Flags), MessageFlags: lnwire.ChanUpdateMsgFlags(edge.Flags >> 8),
ChannelFlags: lnwire.ChanUpdateChanFlags(edge.Flags),
ChannelID: edge.ChannelID, ChannelID: edge.ChannelID,
LastUpdate: testTime, LastUpdate: testTime,
TimeLockDelta: edge.Expiry, TimeLockDelta: edge.Expiry,
@ -487,7 +488,8 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
edgePolicy := &channeldb.ChannelEdgePolicy{ edgePolicy := &channeldb.ChannelEdgePolicy{
SigBytes: testSig.Serialize(), SigBytes: testSig.Serialize(),
Flags: lnwire.ChanUpdateFlag(0), MessageFlags: 0,
ChannelFlags: 0,
ChannelID: channelID, ChannelID: channelID,
LastUpdate: testTime, LastUpdate: testTime,
TimeLockDelta: testChannel.Node1.Expiry, TimeLockDelta: testChannel.Node1.Expiry,
@ -501,7 +503,8 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
edgePolicy = &channeldb.ChannelEdgePolicy{ edgePolicy = &channeldb.ChannelEdgePolicy{
SigBytes: testSig.Serialize(), SigBytes: testSig.Serialize(),
Flags: lnwire.ChanUpdateFlag(lnwire.ChanUpdateDirection), MessageFlags: 0,
ChannelFlags: lnwire.ChanUpdateDirection,
ChannelID: channelID, ChannelID: channelID,
LastUpdate: testTime, LastUpdate: testTime,
TimeLockDelta: testChannel.Node2.Expiry, TimeLockDelta: testChannel.Node2.Expiry,
@ -1476,11 +1479,11 @@ func TestRouteFailDisabledEdge(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch edge: %v", err) t.Fatalf("unable to fetch edge: %v", err)
} }
e1.Flags |= lnwire.ChanUpdateDisabled e1.ChannelFlags |= lnwire.ChanUpdateDisabled
if err := graph.graph.UpdateEdgePolicy(e1); err != nil { if err := graph.graph.UpdateEdgePolicy(e1); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
e2.Flags |= lnwire.ChanUpdateDisabled e2.ChannelFlags |= lnwire.ChanUpdateDisabled
if err := graph.graph.UpdateEdgePolicy(e2); err != nil { if err := graph.graph.UpdateEdgePolicy(e2); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
@ -1507,7 +1510,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch edge: %v", err) t.Fatalf("unable to fetch edge: %v", err)
} }
e.Flags |= lnwire.ChanUpdateDisabled e.ChannelFlags |= lnwire.ChanUpdateDisabled
if err := graph.graph.UpdateEdgePolicy(e); err != nil { if err := graph.graph.UpdateEdgePolicy(e); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
@ -1627,11 +1630,11 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch edge: %v", err) t.Fatalf("unable to fetch edge: %v", err)
} }
e1.Flags |= lnwire.ChanUpdateDisabled e1.ChannelFlags |= lnwire.ChanUpdateDisabled
if err := graph.graph.UpdateEdgePolicy(e1); err != nil { if err := graph.graph.UpdateEdgePolicy(e1); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
e2.Flags |= lnwire.ChanUpdateDisabled e2.ChannelFlags |= lnwire.ChanUpdateDisabled
if err := graph.graph.UpdateEdgePolicy(e2); err != nil { if err := graph.graph.UpdateEdgePolicy(e2); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }

@ -84,7 +84,7 @@ type ChannelGraphSource interface {
// edge for the passed channel ID (and flags) that have a more recent // edge for the passed channel ID (and flags) that have a more recent
// timestamp. // timestamp.
IsStaleEdgePolicy(chanID lnwire.ShortChannelID, timestamp time.Time, IsStaleEdgePolicy(chanID lnwire.ShortChannelID, timestamp time.Time,
flags lnwire.ChanUpdateFlag) bool flags lnwire.ChanUpdateChanFlags) bool
// ForAllOutgoingChannels is used to iterate over all channels // ForAllOutgoingChannels is used to iterate over all channels
// emanating from the "source" node which is the center of the // 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 { func newEdgeLocator(edge *channeldb.ChannelEdgePolicy) *edgeLocator {
return &edgeLocator{ return &edgeLocator{
channelID: edge.ChannelID, 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 // A flag set of 0 indicates this is an announcement for the
// "first" node in the channel. // "first" node in the channel.
case msg.Flags&lnwire.ChanUpdateDirection == 0: case msg.ChannelFlags&lnwire.ChanUpdateDirection == 0:
// Ignore outdated message. // Ignore outdated message.
if !edge1Timestamp.Before(msg.LastUpdate) { if !edge1Timestamp.Before(msg.LastUpdate) {
return newErrf(ErrOutdated, "Ignoring "+ return newErrf(ErrOutdated, "Ignoring "+
"outdated update (flags=%v) for known "+ "outdated update (flags=%v|%v) for "+
"chan_id=%v", msg.Flags, msg.ChannelID) "known chan_id=%v", msg.MessageFlags,
msg.ChannelFlags, msg.ChannelID)
} }
// Similarly, a flag set of 1 indicates this is an announcement // Similarly, a flag set of 1 indicates this is an announcement
// for the "second" node in the channel. // for the "second" node in the channel.
case msg.Flags&lnwire.ChanUpdateDirection == 1: case msg.ChannelFlags&lnwire.ChanUpdateDirection == 1:
// Ignore outdated message. // Ignore outdated message.
if !edge2Timestamp.Before(msg.LastUpdate) { if !edge2Timestamp.Before(msg.LastUpdate) {
return newErrf(ErrOutdated, "Ignoring "+ return newErrf(ErrOutdated, "Ignoring "+
"outdated update (flags=%v) for known "+ "outdated update (flags=%v|%v) for "+
"chan_id=%v", msg.Flags, msg.ChannelID) "known chan_id=%v", msg.MessageFlags,
msg.ChannelFlags, msg.ChannelID)
} }
} }
@ -2059,18 +2060,26 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate,
return true 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) log.Errorf("Unable to validate channel update: %v", err)
return false return false
} }
err := r.UpdateEdge(&channeldb.ChannelEdgePolicy{ err = r.UpdateEdge(&channeldb.ChannelEdgePolicy{
SigBytes: msg.Signature.ToSignatureBytes(), SigBytes: msg.Signature.ToSignatureBytes(),
ChannelID: msg.ShortChannelID.ToUint64(), ChannelID: msg.ShortChannelID.ToUint64(),
LastUpdate: time.Unix(int64(msg.Timestamp), 0), LastUpdate: time.Unix(int64(msg.Timestamp), 0),
Flags: msg.Flags, MessageFlags: msg.MessageFlags,
ChannelFlags: msg.ChannelFlags,
TimeLockDelta: msg.TimeLockDelta, TimeLockDelta: msg.TimeLockDelta,
MinHTLC: msg.HtlcMinimumMsat, MinHTLC: msg.HtlcMinimumMsat,
MaxHTLC: msg.HtlcMaximumMsat,
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee), FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), 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. // NOTE: This method is part of the ChannelGraphSource interface.
func (r *ChannelRouter) IsStaleEdgePolicy(chanID lnwire.ShortChannelID, 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( edge1Timestamp, edge2Timestamp, exists, err := r.cfg.Graph.HasChannelEdge(
chanID.ToUint64(), chanID.ToUint64(),

@ -550,7 +550,8 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
errChanUpdate := lnwire.ChannelUpdate{ errChanUpdate := lnwire.ChannelUpdate{
ShortChannelID: lnwire.NewShortChanIDFromInt(chanID), ShortChannelID: lnwire.NewShortChanIDFromInt(chanID),
Timestamp: uint32(edgeUpateToFail.LastUpdate.Unix()), Timestamp: uint32(edgeUpateToFail.LastUpdate.Unix()),
Flags: edgeUpateToFail.Flags, MessageFlags: edgeUpateToFail.MessageFlags,
ChannelFlags: edgeUpateToFail.ChannelFlags,
TimeLockDelta: edgeUpateToFail.TimeLockDelta, TimeLockDelta: edgeUpateToFail.TimeLockDelta,
HtlcMinimumMsat: edgeUpateToFail.MinHTLC, HtlcMinimumMsat: edgeUpateToFail.MinHTLC,
BaseFee: uint32(edgeUpateToFail.FeeBaseMSat), BaseFee: uint32(edgeUpateToFail.FeeBaseMSat),
@ -656,7 +657,8 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
errChanUpdate := lnwire.ChannelUpdate{ errChanUpdate := lnwire.ChannelUpdate{
ShortChannelID: lnwire.NewShortChanIDFromInt(chanID), ShortChannelID: lnwire.NewShortChanIDFromInt(chanID),
Timestamp: uint32(edgeUpateToFail.LastUpdate.Unix()), Timestamp: uint32(edgeUpateToFail.LastUpdate.Unix()),
Flags: edgeUpateToFail.Flags, MessageFlags: edgeUpateToFail.MessageFlags,
ChannelFlags: edgeUpateToFail.ChannelFlags,
TimeLockDelta: edgeUpateToFail.TimeLockDelta, TimeLockDelta: edgeUpateToFail.TimeLockDelta,
HtlcMinimumMsat: edgeUpateToFail.MinHTLC, HtlcMinimumMsat: edgeUpateToFail.MinHTLC,
BaseFee: uint32(edgeUpateToFail.FeeBaseMSat), BaseFee: uint32(edgeUpateToFail.FeeBaseMSat),
@ -1098,7 +1100,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
} }
edgePolicy.Flags = 0 edgePolicy.ChannelFlags = 0
if err := ctx.router.UpdateEdge(edgePolicy); err != nil { if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
t.Fatalf("unable to update edge policy: %v", err) t.Fatalf("unable to update edge policy: %v", err)
@ -1114,7 +1116,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
} }
edgePolicy.Flags = 1 edgePolicy.ChannelFlags = 1
if err := ctx.router.UpdateEdge(edgePolicy); err != nil { if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
t.Fatalf("unable to update edge policy: %v", err) t.Fatalf("unable to update edge policy: %v", err)
@ -1194,7 +1196,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
} }
edgePolicy.Flags = 0 edgePolicy.ChannelFlags = 0
if err := ctx.router.UpdateEdge(edgePolicy); err != nil { if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
t.Fatalf("unable to update edge policy: %v", err) t.Fatalf("unable to update edge policy: %v", err)
@ -1209,7 +1211,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
} }
edgePolicy.Flags = 1 edgePolicy.ChannelFlags = 1
if err := ctx.router.UpdateEdge(edgePolicy); err != nil { if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
t.Fatalf("unable to update edge policy: %v", err) t.Fatalf("unable to update edge policy: %v", err)
@ -2099,7 +2101,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
} }
edgePolicy.Flags = 0 edgePolicy.ChannelFlags = 0
if err := ctx.router.UpdateEdge(edgePolicy); err != nil { if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
t.Fatalf("unable to update edge policy: %v", err) t.Fatalf("unable to update edge policy: %v", err)
} }
@ -2113,7 +2115,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
FeeBaseMSat: 10, FeeBaseMSat: 10,
FeeProportionalMillionths: 10000, FeeProportionalMillionths: 10000,
} }
edgePolicy.Flags = 1 edgePolicy.ChannelFlags = 1
if err := ctx.router.UpdateEdge(edgePolicy); err != nil { if err := ctx.router.UpdateEdge(edgePolicy); err != nil {
t.Fatalf("unable to update edge policy: %v", err) t.Fatalf("unable to update edge policy: %v", err)
} }

@ -3780,7 +3780,7 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo,
MinHtlc: int64(c1.MinHTLC), MinHtlc: int64(c1.MinHTLC),
FeeBaseMsat: int64(c1.FeeBaseMSat), FeeBaseMsat: int64(c1.FeeBaseMSat),
FeeRateMilliMsat: int64(c1.FeeProportionalMillionths), 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), MinHtlc: int64(c2.MinHTLC),
FeeBaseMsat: int64(c2.FeeBaseMSat), FeeBaseMsat: int64(c2.FeeBaseMSat),
FeeRateMilliMsat: int64(c2.FeeProportionalMillionths), FeeRateMilliMsat: int64(c2.FeeProportionalMillionths),
Disabled: c2.Flags&lnwire.ChanUpdateDisabled != 0, Disabled: c2.ChannelFlags&lnwire.ChanUpdateDisabled != 0,
} }
} }

@ -2978,10 +2978,10 @@ func (s *server) announceChanStatus(op wire.OutPoint, disabled bool) error {
if disabled { if disabled {
// Set the bit responsible for marking a channel as disabled. // Set the bit responsible for marking a channel as disabled.
chanUpdate.Flags |= lnwire.ChanUpdateDisabled chanUpdate.ChannelFlags |= lnwire.ChanUpdateDisabled
} else { } else {
// Clear the bit responsible for marking a channel as disabled. // 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 // 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 { owner := func(edge *channeldb.ChannelEdgePolicy) []byte {
var pubKey *btcec.PublicKey var pubKey *btcec.PublicKey
switch { switch {
case edge.Flags&lnwire.ChanUpdateDirection == 0: case edge.ChannelFlags&lnwire.ChanUpdateDirection == 0:
pubKey, _ = info.NodeKey1() pubKey, _ = info.NodeKey1()
case edge.Flags&lnwire.ChanUpdateDirection == 1: case edge.ChannelFlags&lnwire.ChanUpdateDirection == 1:
pubKey, _ = info.NodeKey2() pubKey, _ = info.NodeKey2()
} }
@ -3100,9 +3100,11 @@ func createChannelUpdate(info *channeldb.ChannelEdgeInfo,
ChainHash: info.ChainHash, ChainHash: info.ChainHash,
ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID), ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID),
Timestamp: uint32(policy.LastUpdate.Unix()), Timestamp: uint32(policy.LastUpdate.Unix()),
Flags: policy.Flags, MessageFlags: policy.MessageFlags,
ChannelFlags: policy.ChannelFlags,
TimeLockDelta: policy.TimeLockDelta, TimeLockDelta: policy.TimeLockDelta,
HtlcMinimumMsat: policy.MinHTLC, HtlcMinimumMsat: policy.MinHTLC,
HtlcMaximumMsat: policy.MaxHTLC,
BaseFee: uint32(policy.FeeBaseMSat), BaseFee: uint32(policy.FeeBaseMSat),
FeeRate: uint32(policy.FeeProportionalMillionths), FeeRate: uint32(policy.FeeProportionalMillionths),
ExtraOpaqueData: policy.ExtraOpaqueData, ExtraOpaqueData: policy.ExtraOpaqueData,