diff --git a/invoices/interface.go b/invoices/interface.go index 4f5e08e32..c906da1c3 100644 --- a/invoices/interface.go +++ b/invoices/interface.go @@ -247,6 +247,12 @@ type HtlcModifyResponse struct { // HTLC was originally sent with, in case additional value is carried // along with it (which might be the case in custom channels). AmountPaid lnwire.MilliSatoshi + + // CancelSet is a flag the interceptor client can set to force a + // cancellation of all HTLCs associated with the invoice that are + // currently accepted. Setting this field will ignore the AmountPaid + // field. + CancelSet bool } // HtlcModifyCallback is a function that is called when an invoice is diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 517e237ca..9d54b6ad8 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -1049,6 +1049,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( return nil, nil, err } + var cancelSet bool + // Provide the invoice to the settlement interceptor to allow // the interceptor's client an opportunity to manipulate the // settlement process. @@ -1066,6 +1068,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( if resp.AmountPaid != 0 { ctx.amtPaid = resp.AmountPaid } + + cancelSet = resp.CancelSet }) if err != nil { err := fmt.Errorf("error during invoice HTLC interception: %w", @@ -1092,7 +1096,18 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( updateDesc.State != nil // Assign resolution to outer scope variable. - resolution = res + if cancelSet { + // If a cancel signal was set for the htlc set, we set + // the resolution as a failure with an underpayment + // indication. Something was wrong with this htlc, so + // we probably can't settle the invoice at all. + resolution = NewFailResolution( + ctx.circuitKey, ctx.currentHeight, + ResultAmountTooLow, + ) + } else { + resolution = res + } return updateDesc, nil } diff --git a/lnrpc/invoicesrpc/htlc_modifier.go b/lnrpc/invoicesrpc/htlc_modifier.go index ce61af468..00259962c 100644 --- a/lnrpc/invoicesrpc/htlc_modifier.go +++ b/lnrpc/invoicesrpc/htlc_modifier.go @@ -82,5 +82,6 @@ func (r *htlcModifier) onIntercept( return &invoices.HtlcModifyResponse{ AmountPaid: amtPaid, + CancelSet: resp.CancelSet, }, nil } diff --git a/lnrpc/invoicesrpc/invoices.pb.go b/lnrpc/invoicesrpc/invoices.pb.go index c23d3a669..ca2b54e7d 100644 --- a/lnrpc/invoicesrpc/invoices.pb.go +++ b/lnrpc/invoicesrpc/invoices.pb.go @@ -782,6 +782,11 @@ type HtlcModifyResponse struct { // HTLC carries other valuable items, as can be the case with custom channel // types. AmtPaid *uint64 `protobuf:"varint,2,opt,name=amt_paid,json=amtPaid,proto3,oneof" json:"amt_paid,omitempty"` + // This flag indicates whether the HTLCs associated with the invoices should + // be cancelled. The interceptor client may set this field if some + // unexpected behavior is encountered. Setting this will ignore the amt_paid + // field. + CancelSet bool `protobuf:"varint,3,opt,name=cancel_set,json=cancelSet,proto3" json:"cancel_set,omitempty"` } func (x *HtlcModifyResponse) Reset() { @@ -830,6 +835,13 @@ func (x *HtlcModifyResponse) GetAmtPaid() uint64 { return 0 } +func (x *HtlcModifyResponse) GetCancelSet() bool { + if x != nil { + return x.CancelSet + } + return false +} + var File_invoicesrpc_invoices_proto protoreflect.FileDescriptor var file_invoicesrpc_invoices_proto_rawDesc = []byte{ @@ -924,14 +936,16 @@ var file_invoicesrpc_invoices_proto_rawDesc = []byte{ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x7b, 0x0a, 0x12, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x69, 0x72, 0x63, - 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, 0x63, - 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, - 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x88, - 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x2a, + 0x38, 0x01, 0x22, 0x9a, 0x01, 0x0a, 0x12, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x69, 0x72, + 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, + 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, + 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, + 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, + 0x65, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x2a, 0x44, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, diff --git a/lnrpc/invoicesrpc/invoices.proto b/lnrpc/invoicesrpc/invoices.proto index 539645f4a..d9d3bc123 100644 --- a/lnrpc/invoicesrpc/invoices.proto +++ b/lnrpc/invoicesrpc/invoices.proto @@ -242,4 +242,10 @@ message HtlcModifyResponse { // HTLC carries other valuable items, as can be the case with custom channel // types. optional uint64 amt_paid = 2; + + // This flag indicates whether the HTLCs associated with the invoices should + // be cancelled. The interceptor client may set this field if some + // unexpected behavior is encountered. Setting this will ignore the amt_paid + // field. + bool cancel_set = 3; } diff --git a/lnrpc/invoicesrpc/invoices.swagger.json b/lnrpc/invoicesrpc/invoices.swagger.json index 4e8cb6ac5..fba415fb3 100644 --- a/lnrpc/invoicesrpc/invoices.swagger.json +++ b/lnrpc/invoicesrpc/invoices.swagger.json @@ -423,6 +423,10 @@ "type": "string", "format": "uint64", "description": "The modified amount in milli-satoshi that the exit HTLC is paying. This\nvalue can be different from the actual on-chain HTLC amount, in case the\nHTLC carries other valuable items, as can be the case with custom channel\ntypes." + }, + "cancel_set": { + "type": "boolean", + "description": "This flag indicates whether the HTLCs associated with the invoices should\nbe cancelled. The interceptor client may set this field if some\nunexpected behavior is encountered. Setting this will ignore the amt_paid\nfield." } } },