From f99d0c4c68cb3391c1848fb011d718b32bcc8145 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Thu, 16 May 2019 15:27:29 +0200 Subject: [PATCH] htlcswitch/switch+payment_result: define networkResult, extractResult --- htlcswitch/payment_result.go | 29 +++++++++- htlcswitch/switch.go | 109 ++++++++++++++++++++--------------- 2 files changed, 89 insertions(+), 49 deletions(-) diff --git a/htlcswitch/payment_result.go b/htlcswitch/payment_result.go index b410c99f..5cfce845 100644 --- a/htlcswitch/payment_result.go +++ b/htlcswitch/payment_result.go @@ -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 +} diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index edea5a58..e7af896f 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -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.