htlcswitch/switch+payment_result: define networkResult, extractResult

This commit is contained in:
Johan T. Halseth 2019-05-16 15:27:29 +02:00
parent 5a8b892bb6
commit f99d0c4c68
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 89 additions and 49 deletions

@ -1,6 +1,10 @@
package htlcswitch
import "errors"
import (
"errors"
"github.com/lightningnetwork/lnd/lnwire"
)
var (
// ErrPaymentIDNotFound is an error returned if the given paymentID is
@ -12,8 +16,9 @@ var (
ErrPaymentIDAlreadyExists = errors.New("paymentID already exists")
)
// PaymentResult wraps a result received from the network after a payment
// attempt was made.
// PaymentResult wraps a decoded result received from the network after a
// payment attempt was made. This is what is eventually handed to the router
// for processing.
type PaymentResult struct {
// Preimage is set by the switch in case a sent HTLC was settled.
Preimage [32]byte
@ -23,3 +28,21 @@ type PaymentResult struct {
// error will be a *ForwardingError.
Error error
}
// networkResult is the raw result received from the network after a payment
// attempt has been made. Since the switch doesn't always have the necessary
// data to decode the raw message, we store it together with some meta data,
// and decode it when the router query for the final result.
type networkResult struct {
// msg is the received result. This should be of type UpdateFulfillHTLC
// or UpdateFailHTLC.
msg lnwire.Message
// unencrypted indicates whether the failure encoded in the message is
// unencrypted, and hence doesn't need to be decrypted.
unencrypted bool
// isResolution indicates whether this is a resolution message, in
// which the failure reason might not be included.
isResolution bool
}

@ -880,53 +880,21 @@ func (s *Switch) handleLocalResponse(pkt *htlcPacket) {
// has been restarted since sending the payment.
payment := s.findPayment(pkt.incomingHTLCID)
var result *PaymentResult
// The error reason will be unencypted in case this a local
// failure or a converted error.
unencrypted := pkt.localFailure || pkt.convertedError
n := &networkResult{
msg: pkt.htlc,
unencrypted: unencrypted,
isResolution: pkt.isResolution,
}
switch htlc := pkt.htlc.(type) {
// We've received a settle update which means we can finalize the user
// payment and return successful response.
case *lnwire.UpdateFulfillHTLC:
// Persistently mark that a payment to this payment hash
// succeeded. This will prevent us from ever making another
// payment to this hash.
err := s.control.Success(pkt.circuit.PaymentHash)
if err != nil && err != ErrPaymentAlreadyCompleted {
log.Warnf("Unable to mark completed payment %x: %v",
pkt.circuit.PaymentHash, err)
return
}
result = &PaymentResult{
Preimage: htlc.PaymentPreimage,
}
// We've received a fail update which means we can finalize the user
// payment and return fail response.
case *lnwire.UpdateFailHTLC:
// Persistently mark that a payment to this payment hash failed.
// This will permit us to make another attempt at a successful
// payment.
err := s.control.Fail(pkt.circuit.PaymentHash)
if err != nil && err != ErrPaymentAlreadyCompleted {
log.Warnf("Unable to ground payment %x: %v",
pkt.circuit.PaymentHash, err)
return
}
// The error reason will be unencypted in case this a local
// failure or a converted error.
unencrypted := pkt.localFailure || pkt.convertedError
paymentErr := s.parseFailedPayment(
payment, pkt.incomingHTLCID, payment.paymentHash,
unencrypted, pkt.isResolution, htlc,
)
result = &PaymentResult{
Error: paymentErr,
}
default:
log.Warnf("Received unknown response type: %T", pkt.htlc)
result, err := s.extractResult(
payment, n, pkt.incomingHTLCID,
pkt.circuit.PaymentHash,
)
if err != nil {
log.Errorf("Unable to extract result: %v", err)
return
}
@ -937,6 +905,55 @@ func (s *Switch) handleLocalResponse(pkt *htlcPacket) {
}
}
// extractResult uses the given deobfuscator to extract the payment result from
// the given network message.
func (s *Switch) extractResult(payment *pendingPayment, n *networkResult,
paymentID uint64, paymentHash lntypes.Hash) (*PaymentResult, error) {
switch htlc := n.msg.(type) {
// We've received a settle update which means we can finalize the user
// payment and return successful response.
case *lnwire.UpdateFulfillHTLC:
// Persistently mark that a payment to this payment hash
// succeeded. This will prevent us from ever making another
// payment to this hash.
err := s.control.Success(paymentHash)
if err != nil && err != ErrPaymentAlreadyCompleted {
return nil, fmt.Errorf("Unable to mark completed "+
"payment %x: %v", paymentHash, err)
}
return &PaymentResult{
Preimage: htlc.PaymentPreimage,
}, nil
// We've received a fail update which means we can finalize the
// user payment and return fail response.
case *lnwire.UpdateFailHTLC:
// Persistently mark that a payment to this payment hash
// failed. This will permit us to make another attempt at a
// successful payment.
err := s.control.Fail(paymentHash)
if err != nil && err != ErrPaymentAlreadyCompleted {
return nil, fmt.Errorf("Unable to ground payment "+
"%x: %v", paymentHash, err)
}
paymentErr := s.parseFailedPayment(
payment, paymentID, payment.paymentHash, n.unencrypted,
n.isResolution, htlc,
)
return &PaymentResult{
Error: paymentErr,
}, nil
default:
return nil, fmt.Errorf("Received unknown response type: %T",
htlc)
}
}
// parseFailedPayment determines the appropriate failure message to return to
// a user initiated payment. The three cases handled are:
// 1) An unencrypted failure, which should already plaintext.