Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions routing/ann_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error {
func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount,
a *lnwire.ChannelUpdate) error {

if err := validateOptionalFields(capacity, a); err != nil {
if err := ValidateOptionalFields(capacity, a); err != nil {
return err
}

Expand Down Expand Up @@ -160,9 +160,9 @@ func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate,
return nil
}

// validateOptionalFields validates a channel update's message flags and
// ValidateOptionalFields validates a channel update's message flags and
// corresponding update fields.
func validateOptionalFields(capacity btcutil.Amount,
func ValidateOptionalFields(capacity btcutil.Amount,
msg *lnwire.ChannelUpdate) error {

if msg.MessageFlags.HasMaxHtlc() {
Expand Down
8 changes: 7 additions & 1 deletion routing/missioncontrol.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package routing

import (
"bytes"
"sync"
"time"

Expand Down Expand Up @@ -188,6 +189,11 @@ func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
// Finally, create the channel edge from the hop hint
// and add it to list of edges corresponding to the node
// at the start of the channel.
v := NewVertex(hopHint.NodeID)
var flags lnwire.ChanUpdateChanFlags
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a rebase I guess?

if bytes.Compare(v[:], endNode.PubKeyBytes[:]) == 1 {
flags |= lnwire.ChanUpdateDirection
}
edge := &channeldb.ChannelEdgePolicy{
Node: endNode,
ChannelID: hopHint.ChannelID,
Expand All @@ -198,9 +204,9 @@ func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
hopHint.FeeProportionalMillionths,
),
TimeLockDelta: hopHint.CLTVExpiryDelta,
ChannelFlags: flags,
}

v := NewVertex(hopHint.NodeID)
edges[v] = append(edges[v], edge)
}
}
Expand Down
12 changes: 12 additions & 0 deletions routing/pathfind.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ type Route struct {
Hops []*Hop
}

// containsChannel returns true if a channel is present in the target route,
// and false otherwise. The passed chanID should be the converted uint64 form
// of lnwire.ShortChannelID.
func (r *Route) containsChannel(chanID uint64) bool {
for _, hop := range r.Hops {
if hop.ChannelID == chanID {
return true
}
}
return false
}

// HopFee returns the fee charged by the route hop indicated by hopIndex.
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
var incomingAmt lnwire.MilliSatoshi
Expand Down
43 changes: 35 additions & 8 deletions routing/pathfind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,14 @@ type testGraph struct {

// testNode represents a node within the test graph above. We skip certain
// information such as the node's IP address as that information isn't needed
// for our tests.
// for our tests. Private keys are optional. If set, they should be consistent
// with the public key. The private key is used to sign error messages
// sent from the node.
type testNode struct {
Source bool `json:"source"`
PubKey string `json:"pubkey"`
Alias string `json:"alias"`
Source bool `json:"source"`
PubKey string `json:"pubkey"`
PrivKey string `json:"privkey"`
Comment thread
joostjager marked this conversation as resolved.
Outdated
Alias string `json:"alias"`
}

// testChan represents the JSON version of a payment channel. This struct
Expand Down Expand Up @@ -164,6 +167,7 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
}

aliasMap := make(map[string]Vertex)
privKeyMap := make(map[string]*btcec.PrivateKey)
var source *channeldb.LightningNode

// First we insert all the nodes within the graph as vertexes.
Expand Down Expand Up @@ -194,6 +198,27 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
// alias map for easy lookup.
aliasMap[node.Alias] = dbNode.PubKeyBytes

// private keys are needed for signing error messages. If set
// check the consistency with the public key.
privBytes, err := hex.DecodeString(node.PrivKey)
if err != nil {
return nil, err
}
if len(privBytes) > 0 {
key, derivedPub := btcec.PrivKeyFromBytes(btcec.S256(),
privBytes)

if !bytes.Equal(dbNode.PubKeyBytes[:],
derivedPub.SerializeCompressed()) {

return nil, fmt.Errorf("%s public key and "+
"private key are inconsistent",
node.Alias)
}

privKeyMap[node.Alias] = key
}

// If the node is tagged as the source, then we create a
// pointer to is so we can mark the source in the graph
// properly.
Expand All @@ -204,7 +229,8 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
// node can be the source in the graph.
if source != nil {
return nil, errors.New("JSON is invalid " +
"multiple nodes are tagged as the source")
"multiple nodes are tagged as the " +
"source")
}

source = dbNode
Expand Down Expand Up @@ -291,9 +317,10 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
}

return &testGraphInstance{
graph: graph,
cleanUp: cleanUp,
aliasMap: aliasMap,
graph: graph,
cleanUp: cleanUp,
aliasMap: aliasMap,
privKeyMap: privKeyMap,
}, nil
}

Expand Down
45 changes: 45 additions & 0 deletions routing/payment_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,51 @@ type paymentSession struct {
pathFinder pathFinder
}

// edgeLocatorOfEdgePolicy returns the edge locator corresponding to
// the channel edge policy
func edgeLocatorOfEdgePolicy(ep *channeldb.ChannelEdgePolicy) *EdgeLocator {
return &EdgeLocator{
ChannelID: ep.ChannelID,
Direction: edgePolicyDirection(ep),
}
}

// edgePolicyDirection returns the direction of the edge policy in the format
// used by edge locators (i.e. 1 if lnwire.ChanUpdateDirection is set)
func edgePolicyDirection(ep *channeldb.ChannelEdgePolicy) uint8 {
if ep.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
return 0
}
return 1
}

// updateEdgePolicy updates the channel edge policy parameters
func updateEdgePolicy(up *lnwire.ChannelUpdate,
ep *channeldb.ChannelEdgePolicy) {

ep.FeeBaseMSat = lnwire.MilliSatoshi(up.BaseFee)
ep.FeeProportionalMillionths = lnwire.MilliSatoshi(up.FeeRate)
ep.TimeLockDelta = up.TimeLockDelta
ep.MinHTLC = lnwire.MilliSatoshi(up.HtlcMinimumMsat)
}

// UpdateEdgePolicy updates edge policy of the routing hints kept in the
// payment session.
func (p *paymentSession) UpdateEdgePolicy(el *EdgeLocator,
update *lnwire.ChannelUpdate) {

log.Debugf("Updating edge %v policy in Mission Control", el)

// Run over hints and update policy if edge id and direction match.
for _, edges := range p.additionalEdges {
for _, edge := range edges {
if *el == *edgeLocatorOfEdgePolicy(edge) {
updateEdgePolicy(update, edge)
}
}
}
}

// ReportVertexFailure adds a vertex to the graph prune view after a client
// reports a routing failure localized to the vertex. The time the vertex was
// added is noted, as it'll be pruned from the shared view after a period of
Expand Down
65 changes: 48 additions & 17 deletions routing/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"crypto/sha256"
"fmt"
"math"
"runtime"
"sort"
"sync"
Expand Down Expand Up @@ -1208,15 +1209,19 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
// Now that we know this isn't a stale update, we'll apply the
// new edge policy to the proper directional edge within the
// channel graph.
if err = r.cfg.Graph.UpdateEdgePolicy(msg); err != nil {
err := errors.Errorf("unable to add channel: %v", err)
log.Error(err)
return err
}
if exists {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a fix in itself? Ideally this could be yet another separate commit, but if you find it too small for that, it's fine with me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to add it as otherwise you would get errors on applyChannelUpdate when trying to update private channels hops.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe the proper fix here is something like this: ad92e5c (feel free to cherry-pick it into this PR if needed).

I believe then you wouldn't have to flip the AssumeChannelValid in the config during tests?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #2549

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed now that #2549 is merged.

err = r.cfg.Graph.UpdateEdgePolicy(msg)
if err != nil {
err := errors.Errorf("unable to update "+
"channel policy: %v", err)
log.Error(err)
return err
}

invalidateCache = true
log.Tracef("New channel update applied: %v",
newLogClosure(func() string { return spew.Sdump(msg) }))
invalidateCache = true
log.Tracef("New channel update applied: %v",
newLogClosure(func() string { return spew.Sdump(msg) }))
}

default:
return errors.Errorf("wrong routing update message type")
Expand Down Expand Up @@ -1805,7 +1810,9 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
// update with id may not be available.
failedEdge, err := getFailedEdge(route, errVertex)
if err != nil {
return true
// If channel identification fails, ignore the error
// message and try again, until timeout.
return false
}

// processChannelUpdateAndRetry is a closure that
Expand All @@ -1824,7 +1831,8 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
pubKey *btcec.PublicKey) {

// Try to apply the channel update.
updateOk := r.applyChannelUpdate(update, pubKey)
updateOk := r.applyChannelUpdate(update,
pubKey, failedEdge)

// If the update could not be applied, prune the
// edge. There is no reason to continue trying
Expand All @@ -1837,8 +1845,13 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
paySession.ReportEdgeFailure(
failedEdge,
)
return
}

// Update routing hints.
paySession.UpdateEdgePolicy(failedEdge, update)

// Report edge policy failure.
paySession.ReportEdgePolicyFailure(
NewVertex(errSource), failedEdge,
)
Expand Down Expand Up @@ -1886,7 +1899,8 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
// that sent us this error, as it doesn't now what the
// correct block height is.
case *lnwire.FailExpiryTooSoon:
r.applyChannelUpdate(&onionErr.Update, errSource)
r.applyChannelUpdate(&onionErr.Update,
errSource, failedEdge)
paySession.ReportVertexFailure(errVertex)
return false

Expand Down Expand Up @@ -1931,15 +1945,17 @@ func (r *ChannelRouter) processSendError(paySession *paymentSession,
// forward one is currently disabled, so we'll apply
// the update and continue.
case *lnwire.FailChannelDisabled:
r.applyChannelUpdate(&onionErr.Update, errSource)
r.applyChannelUpdate(&onionErr.Update,
errSource, failedEdge)
paySession.ReportEdgeFailure(failedEdge)
return false

// It's likely that the outgoing channel didn't have
// sufficient capacity, so we'll prune this edge for
// now, and continue onwards with our path finding.
case *lnwire.FailTemporaryChannelFailure:
r.applyChannelUpdate(onionErr.Update, errSource)
r.applyChannelUpdate(onionErr.Update,
errSource, failedEdge)
paySession.ReportEdgeFailure(failedEdge)
return false

Expand Down Expand Up @@ -2053,27 +2069,42 @@ func getFailedEdge(route *Route, errSource Vertex) (
// applyChannelUpdate validates a channel update and if valid, applies it to the
// database. It returns a bool indicating whether the updates was successful.
func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate,
pubKey *btcec.PublicKey) bool {
pubKey *btcec.PublicKey, e *EdgeLocator) bool {
// If we get passed a nil channel update (as it's optional with some
// onion errors), then we'll exit early with a success result.
if msg == nil {
return true
}

// Verify update signature validates.
if err := ValidateChannelUpdateAnn(pubKey, math.MaxInt64, msg); err != nil {
log.Errorf("Failed to validate channel update: %v", err)
return false
}

// If channel is in database, do further checks. Else, the
// update is for an additional private edge added by hints.
ch, _, _, err := r.GetChannelByID(msg.ShortChannelID)
if err != nil {
log.Errorf("Unable to retrieve channel by id: %v", err)
return false
return true
}

if err := ValidateChannelUpdateAnn(pubKey, ch.Capacity, msg); err != nil {
if err := ValidateOptionalFields(ch.Capacity, msg); err != nil {
log.Errorf("Unable to validate channel update: %v", err)
return false
}

// Trust the direction of the edge locator, and not the one
// in the message.
if e.Direction == 1 {
msg.ChannelFlags |= lnwire.ChanUpdateDirection
} else {
msg.ChannelFlags ^= lnwire.ChanUpdateDirection
}
err = r.UpdateEdge(&channeldb.ChannelEdgePolicy{
SigBytes: msg.Signature.ToSignatureBytes(),
ChannelID: msg.ShortChannelID.ToUint64(),
ChannelID: e.ChannelID,
LastUpdate: time.Unix(int64(msg.Timestamp), 0),
MessageFlags: msg.MessageFlags,
ChannelFlags: msg.ChannelFlags,
Expand Down
Loading