Merge pull request #2565 from joostjager/sendpayment-refactor
routing: sendPayment broken down into multiple functions
This commit is contained in:
commit
cee18892b5
@ -1622,11 +1622,6 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
preImage [32]byte
|
|
||||||
sendError error
|
|
||||||
)
|
|
||||||
|
|
||||||
// We'll also fetch the current block height so we can properly
|
// We'll also fetch the current block height so we can properly
|
||||||
// calculate the required HTLC time locks within the route.
|
// calculate the required HTLC time locks within the route.
|
||||||
_, currentHeight, err := r.cfg.Chain.GetBestBlock()
|
_, currentHeight, err := r.cfg.Chain.GetBestBlock()
|
||||||
@ -1652,6 +1647,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
|
|
||||||
// We'll continue until either our payment succeeds, or we encounter a
|
// We'll continue until either our payment succeeds, or we encounter a
|
||||||
// critical error during path finding.
|
// critical error during path finding.
|
||||||
|
var lastError error
|
||||||
for {
|
for {
|
||||||
// Before we attempt this next payment, we'll check to see if
|
// Before we attempt this next payment, we'll check to see if
|
||||||
// either we've gone past the payment attempt timeout, or the
|
// either we've gone past the payment attempt timeout, or the
|
||||||
@ -1662,12 +1658,12 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
errStr := fmt.Sprintf("payment attempt not completed "+
|
errStr := fmt.Sprintf("payment attempt not completed "+
|
||||||
"before timeout of %v", payAttemptTimeout)
|
"before timeout of %v", payAttemptTimeout)
|
||||||
|
|
||||||
return preImage, nil, newErr(
|
return [32]byte{}, nil, newErr(
|
||||||
ErrPaymentAttemptTimeout, errStr,
|
ErrPaymentAttemptTimeout, errStr,
|
||||||
)
|
)
|
||||||
|
|
||||||
case <-r.quit:
|
case <-r.quit:
|
||||||
return preImage, nil, fmt.Errorf("router shutting down")
|
return [32]byte{}, nil, fmt.Errorf("router shutting down")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Fall through if we haven't hit our time limit, or
|
// Fall through if we haven't hit our time limit, or
|
||||||
@ -1680,279 +1676,315 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// If we're unable to successfully make a payment using
|
// If we're unable to successfully make a payment using
|
||||||
// any of the routes we've found, then return an error.
|
// any of the routes we've found, then return an error.
|
||||||
if sendError != nil {
|
if lastError != nil {
|
||||||
return [32]byte{}, nil, fmt.Errorf("unable to "+
|
return [32]byte{}, nil, fmt.Errorf("unable to "+
|
||||||
"route payment to destination: %v",
|
"route payment to destination: %v",
|
||||||
sendError)
|
lastError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return preImage, nil, err
|
return [32]byte{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("Attempting to send payment %x, using route: %v",
|
// Send payment attempt. It will return a final boolean
|
||||||
payment.PaymentHash, newLogClosure(func() string {
|
// indicating if more attempts are needed.
|
||||||
return spew.Sdump(route)
|
preimage, final, err := r.sendPaymentAttempt(
|
||||||
}),
|
paySession, route, payment.PaymentHash,
|
||||||
)
|
)
|
||||||
|
if final {
|
||||||
// Generate the raw encoded sphinx packet to be included along
|
return preimage, route, err
|
||||||
// with the htlcAdd message that we send directly to the
|
|
||||||
// switch.
|
|
||||||
onionBlob, circuit, err := generateSphinxPacket(
|
|
||||||
route, payment.PaymentHash[:],
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return preImage, nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Craft an HTLC packet to send to the layer 2 switch. The
|
lastError = err
|
||||||
// metadata within this packet will be used to route the
|
}
|
||||||
// payment through the network, starting with the first-hop.
|
}
|
||||||
htlcAdd := &lnwire.UpdateAddHTLC{
|
|
||||||
Amount: route.TotalAmount,
|
|
||||||
Expiry: route.TotalTimeLock,
|
|
||||||
PaymentHash: payment.PaymentHash,
|
|
||||||
}
|
|
||||||
copy(htlcAdd.OnionBlob[:], onionBlob)
|
|
||||||
|
|
||||||
// Attempt to send this payment through the network to complete
|
// sendPaymentAttempt tries to send the payment via the specified route. If
|
||||||
// the payment. If this attempt fails, then we'll continue on
|
// successful, it returns the obtained preimage. If an error occurs, the last
|
||||||
// to the next available route.
|
// bool parameter indicates whether this is a final outcome or more attempts
|
||||||
firstHop := lnwire.NewShortChanIDFromInt(
|
// should be made.
|
||||||
route.Hops[0].ChannelID,
|
func (r *ChannelRouter) sendPaymentAttempt(paySession *paymentSession,
|
||||||
)
|
route *Route, paymentHash [32]byte) ([32]byte, bool, error) {
|
||||||
preImage, sendError = r.cfg.SendToSwitch(
|
|
||||||
firstHop, htlcAdd, circuit,
|
|
||||||
)
|
|
||||||
if sendError != nil {
|
|
||||||
// An error occurred when attempting to send the
|
|
||||||
// payment, depending on the error type, we'll either
|
|
||||||
// continue to send using alternative routes, or simply
|
|
||||||
// terminate this attempt.
|
|
||||||
log.Errorf("Attempt to send payment %x failed: %v",
|
|
||||||
payment.PaymentHash, sendError)
|
|
||||||
|
|
||||||
fErr, ok := sendError.(*htlcswitch.ForwardingError)
|
log.Tracef("Attempting to send payment %x, using route: %v",
|
||||||
if !ok {
|
paymentHash, newLogClosure(func() string {
|
||||||
return preImage, nil, sendError
|
return spew.Sdump(route)
|
||||||
}
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
errSource := fErr.ErrorSource
|
preimage, err := r.sendToSwitch(route, paymentHash)
|
||||||
errVertex := NewVertex(errSource)
|
if err == nil {
|
||||||
|
return preimage, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
log.Tracef("node=%x reported failure when sending "+
|
log.Errorf("Attempt to send payment %x failed: %v",
|
||||||
"htlc=%x", errVertex, payment.PaymentHash[:])
|
paymentHash, err)
|
||||||
|
|
||||||
// Always determine chan id ourselves, because a channel
|
finalOutcome := r.processSendError(paySession, route, err)
|
||||||
// update with id may not be available.
|
|
||||||
failedEdge, err := getFailedEdge(route, errVertex)
|
|
||||||
if err != nil {
|
|
||||||
return preImage, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// processChannelUpdateAndRetry is a closure that
|
return [32]byte{}, finalOutcome, err
|
||||||
// handles a failure message containing a channel
|
}
|
||||||
// update. This function always tries to apply the
|
|
||||||
// channel update and passes on the result to the
|
|
||||||
// payment session to adjust its view on the reliability
|
|
||||||
// of the network.
|
|
||||||
//
|
|
||||||
// As channel id, the locally determined channel id is
|
|
||||||
// used. It does not rely on the channel id that is part
|
|
||||||
// of the channel update message, because the remote
|
|
||||||
// node may lie to us or the update may be corrupt.
|
|
||||||
processChannelUpdateAndRetry := func(
|
|
||||||
update *lnwire.ChannelUpdate,
|
|
||||||
pubKey *btcec.PublicKey) {
|
|
||||||
|
|
||||||
// Try to apply the channel update.
|
// sendToSwitch sends a payment along the specified route and returns the
|
||||||
updateOk := r.applyChannelUpdate(update, pubKey)
|
// obtained preimage.
|
||||||
|
func (r *ChannelRouter) sendToSwitch(route *Route, paymentHash [32]byte) (
|
||||||
|
[32]byte, error) {
|
||||||
|
|
||||||
// If the update could not be applied, prune the
|
// Generate the raw encoded sphinx packet to be included along
|
||||||
// edge. There is no reason to continue trying
|
// with the htlcAdd message that we send directly to the
|
||||||
// this channel.
|
// switch.
|
||||||
//
|
onionBlob, circuit, err := generateSphinxPacket(
|
||||||
// TODO: Could even prune the node completely?
|
route, paymentHash[:],
|
||||||
// Or is there a valid reason for the channel
|
)
|
||||||
// update to fail?
|
if err != nil {
|
||||||
if !updateOk {
|
return [32]byte{}, err
|
||||||
paySession.ReportEdgeFailure(
|
}
|
||||||
failedEdge,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
paySession.ReportEdgePolicyFailure(
|
// Craft an HTLC packet to send to the layer 2 switch. The
|
||||||
NewVertex(errSource), failedEdge,
|
// metadata within this packet will be used to route the
|
||||||
)
|
// payment through the network, starting with the first-hop.
|
||||||
}
|
htlcAdd := &lnwire.UpdateAddHTLC{
|
||||||
|
Amount: route.TotalAmount,
|
||||||
|
Expiry: route.TotalTimeLock,
|
||||||
|
PaymentHash: paymentHash,
|
||||||
|
}
|
||||||
|
copy(htlcAdd.OnionBlob[:], onionBlob)
|
||||||
|
|
||||||
switch onionErr := fErr.FailureMessage.(type) {
|
// Attempt to send this payment through the network to complete
|
||||||
// If the end destination didn't know the payment
|
// the payment. If this attempt fails, then we'll continue on
|
||||||
// hash or we sent the wrong payment amount to the
|
// to the next available route.
|
||||||
// destination, then we'll terminate immediately.
|
firstHop := lnwire.NewShortChanIDFromInt(
|
||||||
case *lnwire.FailUnknownPaymentHash:
|
route.Hops[0].ChannelID,
|
||||||
return preImage, nil, sendError
|
)
|
||||||
|
return r.cfg.SendToSwitch(
|
||||||
|
firstHop, htlcAdd, circuit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// If we sent the wrong amount to the destination, then
|
// processSendError analyzes the error for the payment attempt received from the
|
||||||
// we'll exit early.
|
// switch and updates mission control and/or channel policies. Depending on the
|
||||||
case *lnwire.FailIncorrectPaymentAmount:
|
// error type, this error is either the final outcome of the payment or we need
|
||||||
return preImage, nil, sendError
|
// to continue with an alternative route. This is indicated by the boolean
|
||||||
|
// return value.
|
||||||
|
func (r *ChannelRouter) processSendError(paySession *paymentSession,
|
||||||
|
route *Route, err error) bool {
|
||||||
|
|
||||||
// If the time-lock that was extended to the final node
|
fErr, ok := err.(*htlcswitch.ForwardingError)
|
||||||
// was incorrect, then we can't proceed.
|
if !ok {
|
||||||
case *lnwire.FailFinalIncorrectCltvExpiry:
|
return true
|
||||||
return preImage, nil, sendError
|
}
|
||||||
|
|
||||||
// If we crafted an invalid onion payload for the final
|
errSource := fErr.ErrorSource
|
||||||
// node, then we'll exit early.
|
errVertex := NewVertex(errSource)
|
||||||
case *lnwire.FailFinalIncorrectHtlcAmount:
|
|
||||||
return preImage, nil, sendError
|
|
||||||
|
|
||||||
// Similarly, if the HTLC expiry that we extended to
|
log.Tracef("node=%x reported failure when sending htlc", errVertex)
|
||||||
// the final hop expires too soon, then will fail the
|
|
||||||
// payment.
|
|
||||||
//
|
|
||||||
// TODO(roasbeef): can happen to to race condition, try
|
|
||||||
// again with recent block height
|
|
||||||
case *lnwire.FailFinalExpiryTooSoon:
|
|
||||||
return preImage, nil, sendError
|
|
||||||
|
|
||||||
// If we erroneously attempted to cross a chain border,
|
// Always determine chan id ourselves, because a channel
|
||||||
// then we'll cancel the payment.
|
// update with id may not be available.
|
||||||
case *lnwire.FailInvalidRealm:
|
failedEdge, err := getFailedEdge(route, errVertex)
|
||||||
return preImage, nil, sendError
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// If we get a notice that the expiry was too soon for
|
// processChannelUpdateAndRetry is a closure that
|
||||||
// an intermediate node, then we'll prune out the node
|
// handles a failure message containing a channel
|
||||||
// that sent us this error, as it doesn't now what the
|
// update. This function always tries to apply the
|
||||||
// correct block height is.
|
// channel update and passes on the result to the
|
||||||
case *lnwire.FailExpiryTooSoon:
|
// payment session to adjust its view on the reliability
|
||||||
r.applyChannelUpdate(&onionErr.Update, errSource)
|
// of the network.
|
||||||
paySession.ReportVertexFailure(errVertex)
|
//
|
||||||
continue
|
// As channel id, the locally determined channel id is
|
||||||
|
// used. It does not rely on the channel id that is part
|
||||||
|
// of the channel update message, because the remote
|
||||||
|
// node may lie to us or the update may be corrupt.
|
||||||
|
processChannelUpdateAndRetry := func(
|
||||||
|
update *lnwire.ChannelUpdate,
|
||||||
|
pubKey *btcec.PublicKey) {
|
||||||
|
|
||||||
// If we hit an instance of onion payload corruption or
|
// Try to apply the channel update.
|
||||||
// an invalid version, then we'll exit early as this
|
updateOk := r.applyChannelUpdate(update, pubKey)
|
||||||
// shouldn't happen in the typical case.
|
|
||||||
case *lnwire.FailInvalidOnionVersion:
|
|
||||||
return preImage, nil, sendError
|
|
||||||
case *lnwire.FailInvalidOnionHmac:
|
|
||||||
return preImage, nil, sendError
|
|
||||||
case *lnwire.FailInvalidOnionKey:
|
|
||||||
return preImage, nil, sendError
|
|
||||||
|
|
||||||
// If we get a failure due to violating the minimum
|
// If the update could not be applied, prune the
|
||||||
// amount, we'll apply the new minimum amount and retry
|
// edge. There is no reason to continue trying
|
||||||
// routing.
|
// this channel.
|
||||||
case *lnwire.FailAmountBelowMinimum:
|
//
|
||||||
processChannelUpdateAndRetry(
|
// TODO: Could even prune the node completely?
|
||||||
&onionErr.Update, errSource,
|
// Or is there a valid reason for the channel
|
||||||
)
|
// update to fail?
|
||||||
continue
|
if !updateOk {
|
||||||
|
paySession.ReportEdgeFailure(
|
||||||
// If we get a failure due to a fee, we'll apply the
|
failedEdge,
|
||||||
// new fee update, and retry our attempt using the
|
)
|
||||||
// newly updated fees.
|
|
||||||
case *lnwire.FailFeeInsufficient:
|
|
||||||
processChannelUpdateAndRetry(
|
|
||||||
&onionErr.Update, errSource,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If we get the failure for an intermediate node that
|
|
||||||
// disagrees with our time lock values, then we'll
|
|
||||||
// apply the new delta value and try it once more.
|
|
||||||
case *lnwire.FailIncorrectCltvExpiry:
|
|
||||||
processChannelUpdateAndRetry(
|
|
||||||
&onionErr.Update, errSource,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// The outgoing channel that this node was meant to
|
|
||||||
// forward one is currently disabled, so we'll apply
|
|
||||||
// the update and continue.
|
|
||||||
case *lnwire.FailChannelDisabled:
|
|
||||||
r.applyChannelUpdate(&onionErr.Update, errSource)
|
|
||||||
paySession.ReportEdgeFailure(failedEdge)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
paySession.ReportEdgeFailure(failedEdge)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If the send fail due to a node not having the
|
|
||||||
// required features, then we'll note this error and
|
|
||||||
// continue.
|
|
||||||
case *lnwire.FailRequiredNodeFeatureMissing:
|
|
||||||
paySession.ReportVertexFailure(errVertex)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If the send fail due to a node not having the
|
|
||||||
// required features, then we'll note this error and
|
|
||||||
// continue.
|
|
||||||
case *lnwire.FailRequiredChannelFeatureMissing:
|
|
||||||
paySession.ReportVertexFailure(errVertex)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If the next hop in the route wasn't known or
|
|
||||||
// offline, we'll only the channel which we attempted
|
|
||||||
// to route over. This is conservative, and it can
|
|
||||||
// handle faulty channels between nodes properly.
|
|
||||||
// Additionally, this guards against routing nodes
|
|
||||||
// returning errors in order to attempt to black list
|
|
||||||
// another node.
|
|
||||||
case *lnwire.FailUnknownNextPeer:
|
|
||||||
paySession.ReportEdgeFailure(failedEdge)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If the node wasn't able to forward for which ever
|
|
||||||
// reason, then we'll note this and continue with the
|
|
||||||
// routes.
|
|
||||||
case *lnwire.FailTemporaryNodeFailure:
|
|
||||||
paySession.ReportVertexFailure(errVertex)
|
|
||||||
continue
|
|
||||||
|
|
||||||
case *lnwire.FailPermanentNodeFailure:
|
|
||||||
paySession.ReportVertexFailure(errVertex)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If we crafted a route that contains a too long time
|
|
||||||
// lock for an intermediate node, we'll prune the node.
|
|
||||||
// As there currently is no way of knowing that node's
|
|
||||||
// maximum acceptable cltv, we cannot take this
|
|
||||||
// constraint into account during routing.
|
|
||||||
//
|
|
||||||
// TODO(joostjager): Record the rejected cltv and use
|
|
||||||
// that as a hint during future path finding through
|
|
||||||
// that node.
|
|
||||||
case *lnwire.FailExpiryTooFar:
|
|
||||||
paySession.ReportVertexFailure(errVertex)
|
|
||||||
continue
|
|
||||||
|
|
||||||
// If we get a permanent channel or node failure, then
|
|
||||||
// we'll prune the channel in both directions and
|
|
||||||
// continue with the rest of the routes.
|
|
||||||
case *lnwire.FailPermanentChannelFailure:
|
|
||||||
paySession.ReportEdgeFailure(&edgeLocator{
|
|
||||||
channelID: failedEdge.channelID,
|
|
||||||
direction: 0,
|
|
||||||
})
|
|
||||||
paySession.ReportEdgeFailure(&edgeLocator{
|
|
||||||
channelID: failedEdge.channelID,
|
|
||||||
direction: 1,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
|
|
||||||
default:
|
|
||||||
return preImage, nil, sendError
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return preImage, route, nil
|
paySession.ReportEdgePolicyFailure(
|
||||||
|
NewVertex(errSource), failedEdge,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch onionErr := fErr.FailureMessage.(type) {
|
||||||
|
|
||||||
|
// If the end destination didn't know the payment
|
||||||
|
// hash or we sent the wrong payment amount to the
|
||||||
|
// destination, then we'll terminate immediately.
|
||||||
|
case *lnwire.FailUnknownPaymentHash:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If we sent the wrong amount to the destination, then
|
||||||
|
// we'll exit early.
|
||||||
|
case *lnwire.FailIncorrectPaymentAmount:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If the time-lock that was extended to the final node
|
||||||
|
// was incorrect, then we can't proceed.
|
||||||
|
case *lnwire.FailFinalIncorrectCltvExpiry:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If we crafted an invalid onion payload for the final
|
||||||
|
// node, then we'll exit early.
|
||||||
|
case *lnwire.FailFinalIncorrectHtlcAmount:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Similarly, if the HTLC expiry that we extended to
|
||||||
|
// the final hop expires too soon, then will fail the
|
||||||
|
// payment.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): can happen to to race condition, try
|
||||||
|
// again with recent block height
|
||||||
|
case *lnwire.FailFinalExpiryTooSoon:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If we erroneously attempted to cross a chain border,
|
||||||
|
// then we'll cancel the payment.
|
||||||
|
case *lnwire.FailInvalidRealm:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If we get a notice that the expiry was too soon for
|
||||||
|
// an intermediate node, then we'll prune out the node
|
||||||
|
// that sent us this error, as it doesn't now what the
|
||||||
|
// correct block height is.
|
||||||
|
case *lnwire.FailExpiryTooSoon:
|
||||||
|
r.applyChannelUpdate(&onionErr.Update, errSource)
|
||||||
|
paySession.ReportVertexFailure(errVertex)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If we hit an instance of onion payload corruption or
|
||||||
|
// an invalid version, then we'll exit early as this
|
||||||
|
// shouldn't happen in the typical case.
|
||||||
|
case *lnwire.FailInvalidOnionVersion:
|
||||||
|
return true
|
||||||
|
case *lnwire.FailInvalidOnionHmac:
|
||||||
|
return true
|
||||||
|
case *lnwire.FailInvalidOnionKey:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If we get a failure due to violating the minimum
|
||||||
|
// amount, we'll apply the new minimum amount and retry
|
||||||
|
// routing.
|
||||||
|
case *lnwire.FailAmountBelowMinimum:
|
||||||
|
processChannelUpdateAndRetry(
|
||||||
|
&onionErr.Update, errSource,
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If we get a failure due to a fee, we'll apply the
|
||||||
|
// new fee update, and retry our attempt using the
|
||||||
|
// newly updated fees.
|
||||||
|
case *lnwire.FailFeeInsufficient:
|
||||||
|
processChannelUpdateAndRetry(
|
||||||
|
&onionErr.Update, errSource,
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If we get the failure for an intermediate node that
|
||||||
|
// disagrees with our time lock values, then we'll
|
||||||
|
// apply the new delta value and try it once more.
|
||||||
|
case *lnwire.FailIncorrectCltvExpiry:
|
||||||
|
processChannelUpdateAndRetry(
|
||||||
|
&onionErr.Update, errSource,
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// The outgoing channel that this node was meant to
|
||||||
|
// forward one is currently disabled, so we'll apply
|
||||||
|
// the update and continue.
|
||||||
|
case *lnwire.FailChannelDisabled:
|
||||||
|
r.applyChannelUpdate(&onionErr.Update, errSource)
|
||||||
|
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)
|
||||||
|
paySession.ReportEdgeFailure(failedEdge)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If the send fail due to a node not having the
|
||||||
|
// required features, then we'll note this error and
|
||||||
|
// continue.
|
||||||
|
case *lnwire.FailRequiredNodeFeatureMissing:
|
||||||
|
paySession.ReportVertexFailure(errVertex)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If the send fail due to a node not having the
|
||||||
|
// required features, then we'll note this error and
|
||||||
|
// continue.
|
||||||
|
case *lnwire.FailRequiredChannelFeatureMissing:
|
||||||
|
paySession.ReportVertexFailure(errVertex)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If the next hop in the route wasn't known or
|
||||||
|
// offline, we'll only the channel which we attempted
|
||||||
|
// to route over. This is conservative, and it can
|
||||||
|
// handle faulty channels between nodes properly.
|
||||||
|
// Additionally, this guards against routing nodes
|
||||||
|
// returning errors in order to attempt to black list
|
||||||
|
// another node.
|
||||||
|
case *lnwire.FailUnknownNextPeer:
|
||||||
|
paySession.ReportEdgeFailure(failedEdge)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If the node wasn't able to forward for which ever
|
||||||
|
// reason, then we'll note this and continue with the
|
||||||
|
// routes.
|
||||||
|
case *lnwire.FailTemporaryNodeFailure:
|
||||||
|
paySession.ReportVertexFailure(errVertex)
|
||||||
|
return false
|
||||||
|
|
||||||
|
case *lnwire.FailPermanentNodeFailure:
|
||||||
|
paySession.ReportVertexFailure(errVertex)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If we crafted a route that contains a too long time
|
||||||
|
// lock for an intermediate node, we'll prune the node.
|
||||||
|
// As there currently is no way of knowing that node's
|
||||||
|
// maximum acceptable cltv, we cannot take this
|
||||||
|
// constraint into account during routing.
|
||||||
|
//
|
||||||
|
// TODO(joostjager): Record the rejected cltv and use
|
||||||
|
// that as a hint during future path finding through
|
||||||
|
// that node.
|
||||||
|
case *lnwire.FailExpiryTooFar:
|
||||||
|
paySession.ReportVertexFailure(errVertex)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// If we get a permanent channel or node failure, then
|
||||||
|
// we'll prune the channel in both directions and
|
||||||
|
// continue with the rest of the routes.
|
||||||
|
case *lnwire.FailPermanentChannelFailure:
|
||||||
|
paySession.ReportEdgeFailure(&edgeLocator{
|
||||||
|
channelID: failedEdge.channelID,
|
||||||
|
direction: 0,
|
||||||
|
})
|
||||||
|
paySession.ReportEdgeFailure(&edgeLocator{
|
||||||
|
channelID: failedEdge.channelID,
|
||||||
|
direction: 1,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user