9
9
"time"
10
10
11
11
"github.com/lightningnetwork/lnd/clock"
12
+ "github.com/lightningnetwork/lnd/fn/v2"
12
13
"github.com/lightningnetwork/lnd/lntypes"
13
14
"github.com/lightningnetwork/lnd/lnwire"
14
15
"github.com/lightningnetwork/lnd/queue"
@@ -653,56 +654,38 @@ func (i *InvoiceRegistry) startHtlcTimer(invoiceRef InvoiceRef,
653
654
func (i * InvoiceRegistry ) cancelSingleHtlc (invoiceRef InvoiceRef ,
654
655
key CircuitKey , result FailResolutionResult ) error {
655
656
656
- updateInvoice := func (invoice * Invoice ) (* InvoiceUpdateDesc , error ) {
657
+ updateInvoice := func (invoice * Invoice , setID * SetID ) (
658
+ * InvoiceUpdateDesc , error ) {
659
+
657
660
// Only allow individual htlc cancellation on open invoices.
658
661
if invoice .State != ContractOpen {
659
- log .Debugf ("cancelSingleHtlc: invoice %v no longer " +
660
- "open" , invoiceRef )
662
+ log .Debugf ("CancelSingleHtlc: cannot cancel htlc %v " +
663
+ "on invoice %v, invoice is no longer open" , key ,
664
+ invoiceRef )
661
665
662
666
return nil , nil
663
667
}
664
668
665
- // Lookup the current status of the htlc in the database.
666
- var (
667
- htlcState HtlcState
668
- setID * SetID
669
- )
669
+ // Also for AMP invoices we fetch the relevant HTLCs, so
670
+ // the HTLC should be found, otherwise we return an error.
670
671
htlc , ok := invoice .Htlcs [key ]
671
672
if ! ok {
672
- // If this is an AMP invoice, then all the HTLCs won't
673
- // be read out, so we'll consult the other mapping to
674
- // try to find the HTLC state in question here.
675
- var found bool
676
- for ampSetID , htlcSet := range invoice .AMPState {
677
- ampSetID := ampSetID
678
- for htlcKey := range htlcSet .InvoiceKeys {
679
- if htlcKey == key {
680
- htlcState = htlcSet .State
681
- setID = & ampSetID
682
-
683
- found = true
684
- break
685
- }
686
- }
687
- }
688
-
689
- if ! found {
690
- return nil , fmt .Errorf ("htlc %v not found" , key )
691
- }
692
- } else {
693
- htlcState = htlc .State
673
+ return nil , fmt .Errorf ("htlc %v not found on " +
674
+ "invoice %v" , key , invoiceRef )
694
675
}
695
676
677
+ htlcState := htlc .State
678
+
696
679
// Cancellation is only possible if the htlc wasn't already
697
680
// resolved.
698
681
if htlcState != HtlcStateAccepted {
699
- log .Debugf ("cancelSingleHtlc : htlc %v on invoice %v " +
682
+ log .Debugf ("CancelSingleHtlc : htlc %v on invoice %v " +
700
683
"is already resolved" , key , invoiceRef )
701
684
702
685
return nil , nil
703
686
}
704
687
705
- log .Debugf ("cancelSingleHtlc : cancelling htlc %v on invoice %v" ,
688
+ log .Debugf ("CancelSingleHtlc : cancelling htlc %v on invoice %v" ,
706
689
key , invoiceRef )
707
690
708
691
// Return an update descriptor that cancels htlc and keeps
@@ -728,7 +711,7 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef InvoiceRef,
728
711
func (invoice * Invoice ) (
729
712
* InvoiceUpdateDesc , error ) {
730
713
731
- updateDesc , err := updateInvoice (invoice )
714
+ updateDesc , err := updateInvoice (invoice , setID )
732
715
if err != nil {
733
716
return nil , err
734
717
}
@@ -755,8 +738,13 @@ func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef InvoiceRef,
755
738
key , int32 (htlc .AcceptHeight ), result ,
756
739
)
757
740
741
+ log .Debugf ("Signaling htlc(%v) cancellation of invoice(%v) " +
742
+ "with resolution(%v) to the link subsystem" , key ,
743
+ invoiceRef , result )
744
+
758
745
i .notifyHodlSubscribers (resolution )
759
746
}
747
+
760
748
return nil
761
749
}
762
750
@@ -1086,29 +1074,83 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked(
1086
1074
updateSubscribers bool
1087
1075
)
1088
1076
callback := func (inv * Invoice ) (* InvoiceUpdateDesc , error ) {
1089
- updateDesc , res , err := updateInvoice (ctx , inv )
1077
+ // First check if this is a replayed htlc and resolve it
1078
+ // according to its current state. We cannot decide differently
1079
+ // once the HTLC has already been processed before.
1080
+ isReplayed , res , err := resolveReplayedHtlc (ctx , inv )
1090
1081
if err != nil {
1091
1082
return nil , err
1092
1083
}
1084
+ if isReplayed {
1085
+ resolution = res
1086
+ return nil , nil
1087
+ }
1093
1088
1094
- // Only send an update if the invoice state was changed.
1095
- updateSubscribers = updateDesc != nil &&
1096
- updateDesc .State != nil
1097
-
1098
- // Assign resolution to outer scope variable.
1089
+ // In case the HTLC interceptor cancels the HTLC set, we do NOT
1090
+ // cancel the invoice however we cancel the complete HTLC set.
1099
1091
if cancelSet {
1100
- // If a cancel signal was set for the htlc set, we set
1101
- // the resolution as a failure with an underpayment
1102
- // indication. Something was wrong with this htlc, so
1103
- // we probably can't settle the invoice at all.
1092
+ // If the invoice is not open, something is wrong, we
1093
+ // fail just the HTLC with the specific error.
1094
+ if inv .State != ContractOpen {
1095
+ log .Errorf ("Invoice state (%v) is not OPEN, " +
1096
+ "cancelling HTLC set not allowed by " +
1097
+ "external source" , inv .State )
1098
+
1099
+ resolution = NewFailResolution (
1100
+ ctx .circuitKey , ctx .currentHeight ,
1101
+ ResultInvoiceNotOpen ,
1102
+ )
1103
+
1104
+ return nil , nil
1105
+ }
1106
+
1107
+ // The error `ExternalValidationFailed` error
1108
+ // information will be packed in the
1109
+ // `FailIncorrectDetails` msg when sending the msg to
1110
+ // the peer. Error codes are defined by the BOLT 04
1111
+ // specification. The error text can be arbitrary
1112
+ // therefore we return a custom error msg.
1104
1113
resolution = NewFailResolution (
1105
1114
ctx .circuitKey , ctx .currentHeight ,
1106
- ResultAmountTooLow ,
1115
+ ExternalValidationFailed ,
1107
1116
)
1108
- } else {
1109
- resolution = res
1117
+
1118
+ // We cancel all HTLCs which are in the accepted state.
1119
+ //
1120
+ // NOTE: The current HTLC is not included because it
1121
+ // was never accepted in the first place.
1122
+ htlcs := inv .HTLCSet (ctx .setID (), HtlcStateAccepted )
1123
+ htlcKeys := fn.KeySet [CircuitKey ](htlcs )
1124
+
1125
+ // The external source did cancel the htlc set, so we
1126
+ // cancel all HTLCs in the set. We however keep the
1127
+ // invoice in the open state.
1128
+ //
1129
+ // NOTE: The invoice event loop will still call the
1130
+ // `cancelSingleHTLC` method for MPP payments, however
1131
+ // because the HTLCs are already cancled back it will be
1132
+ // a NOOP.
1133
+ update := & InvoiceUpdateDesc {
1134
+ UpdateType : CancelHTLCsUpdate ,
1135
+ CancelHtlcs : htlcKeys ,
1136
+ SetID : setID ,
1137
+ }
1138
+
1139
+ return update , nil
1110
1140
}
1111
1141
1142
+ updateDesc , res , err := updateInvoice (ctx , inv )
1143
+ if err != nil {
1144
+ return nil , err
1145
+ }
1146
+
1147
+ // Set resolution in outer scope only after successful update.
1148
+ resolution = res
1149
+
1150
+ // Only send an update if the invoice state was changed.
1151
+ updateSubscribers = updateDesc != nil &&
1152
+ updateDesc .State != nil
1153
+
1112
1154
return updateDesc , nil
1113
1155
}
1114
1156
@@ -1417,6 +1459,8 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(ctx context.Context,
1417
1459
}
1418
1460
1419
1461
invoiceRef := InvoiceRefByHash (payHash )
1462
+
1463
+ // We pass a nil setID which means no HTLCs will be read out.
1420
1464
invoice , err := i .idb .UpdateInvoice (ctx , invoiceRef , nil , updateInvoice )
1421
1465
1422
1466
// Implement idempotency by returning success if the invoice was already
@@ -1443,6 +1487,8 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(ctx context.Context,
1443
1487
// that are waiting for resolution. Any htlcs that were already canceled
1444
1488
// before, will be notified again. This isn't necessary but doesn't hurt
1445
1489
// either.
1490
+ //
1491
+ // TODO(ziggie): Also consider AMP HTLCs here.
1446
1492
for key , htlc := range invoice .Htlcs {
1447
1493
if htlc .State != HtlcStateCanceled {
1448
1494
continue
0 commit comments