2019-12-03 12:22:31 +01:00
|
|
|
package invoices
|
|
|
|
|
|
|
|
import (
|
2024-05-05 14:48:50 +02:00
|
|
|
"bytes"
|
2022-01-11 14:15:23 +01:00
|
|
|
"encoding/hex"
|
2019-12-03 12:22:31 +01:00
|
|
|
"errors"
|
|
|
|
|
2024-05-05 14:48:50 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
2021-03-25 03:52:01 +01:00
|
|
|
"github.com/lightningnetwork/lnd/amp"
|
2020-02-04 15:15:48 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2019-12-03 12:22:31 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2019-09-03 12:23:39 +02:00
|
|
|
"github.com/lightningnetwork/lnd/record"
|
2019-12-03 12:22:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// invoiceUpdateCtx is an object that describes the context for the invoice
|
|
|
|
// update to be carried out.
|
|
|
|
type invoiceUpdateCtx struct {
|
2020-02-04 15:15:48 +01:00
|
|
|
hash lntypes.Hash
|
2022-11-30 12:00:37 +01:00
|
|
|
circuitKey CircuitKey
|
2019-12-03 12:22:31 +01:00
|
|
|
amtPaid lnwire.MilliSatoshi
|
|
|
|
expiry uint32
|
|
|
|
currentHeight int32
|
|
|
|
finalCltvRejectDelta int32
|
2024-05-08 19:17:05 +02:00
|
|
|
|
|
|
|
// wireCustomRecords are the custom records that were included with the
|
|
|
|
// HTLC wire message.
|
|
|
|
wireCustomRecords lnwire.CustomRecords
|
|
|
|
|
|
|
|
// customRecords is a map of custom records that were included with the
|
|
|
|
// HTLC onion payload.
|
|
|
|
customRecords record.CustomSet
|
|
|
|
|
|
|
|
mpp *record.MPP
|
|
|
|
amp *record.AMP
|
|
|
|
metadata []byte
|
|
|
|
pathID *chainhash.Hash
|
|
|
|
totalAmtMsat lnwire.MilliSatoshi
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
2020-05-22 00:37:10 +02:00
|
|
|
// invoiceRef returns an identifier that can be used to lookup or update the
|
|
|
|
// invoice this HTLC is targeting.
|
2022-11-30 12:00:37 +01:00
|
|
|
func (i *invoiceUpdateCtx) invoiceRef() InvoiceRef {
|
2021-03-25 03:52:01 +01:00
|
|
|
switch {
|
2024-05-05 14:48:50 +02:00
|
|
|
case i.pathID != nil:
|
|
|
|
return InvoiceRefByHashAndAddr(i.hash, *i.pathID)
|
|
|
|
|
2021-03-25 03:52:01 +01:00
|
|
|
case i.amp != nil && i.mpp != nil:
|
|
|
|
payAddr := i.mpp.PaymentAddr()
|
2022-11-30 12:00:37 +01:00
|
|
|
return InvoiceRefByAddr(payAddr)
|
|
|
|
|
2021-03-25 03:52:01 +01:00
|
|
|
case i.mpp != nil:
|
2020-05-22 00:37:39 +02:00
|
|
|
payAddr := i.mpp.PaymentAddr()
|
2022-11-30 12:00:37 +01:00
|
|
|
return InvoiceRefByHashAndAddr(i.hash, payAddr)
|
|
|
|
|
2021-03-25 03:52:01 +01:00
|
|
|
default:
|
2022-11-30 12:00:37 +01:00
|
|
|
return InvoiceRefByHash(i.hash)
|
2020-05-22 00:37:39 +02:00
|
|
|
}
|
2020-05-22 00:37:10 +02:00
|
|
|
}
|
|
|
|
|
2021-03-03 18:59:29 +01:00
|
|
|
// setID returns an identifier that identifies other possible HTLCs that this
|
|
|
|
// particular one is related to. If nil is returned this means the HTLC is an
|
|
|
|
// MPP or legacy payment, otherwise the HTLC belongs AMP payment.
|
|
|
|
func (i invoiceUpdateCtx) setID() *[32]byte {
|
|
|
|
if i.amp != nil {
|
|
|
|
setID := i.amp.SetID()
|
|
|
|
return &setID
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-04 15:17:54 +01:00
|
|
|
// log logs a message specific to this update context.
|
|
|
|
func (i *invoiceUpdateCtx) log(s string) {
|
2022-01-11 14:15:23 +01:00
|
|
|
// Don't use %x in the log statement below, because it doesn't
|
|
|
|
// distinguish between nil and empty metadata.
|
|
|
|
metadata := "<nil>"
|
|
|
|
if i.metadata != nil {
|
|
|
|
metadata = hex.EncodeToString(i.metadata)
|
|
|
|
}
|
|
|
|
|
2021-03-03 18:54:11 +01:00
|
|
|
log.Debugf("Invoice%v: %v, amt=%v, expiry=%v, circuit=%v, mpp=%v, "+
|
2022-01-11 14:15:23 +01:00
|
|
|
"amp=%v, metadata=%v", i.invoiceRef(), s, i.amtPaid, i.expiry,
|
|
|
|
i.circuitKey, i.mpp, i.amp, metadata)
|
2020-02-04 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2020-02-06 18:35:10 +01:00
|
|
|
// failRes is a helper function which creates a failure resolution with
|
2020-02-06 18:35:16 +01:00
|
|
|
// the information contained in the invoiceUpdateCtx and the fail resolution
|
|
|
|
// result provided.
|
|
|
|
func (i invoiceUpdateCtx) failRes(outcome FailResolutionResult) *HtlcFailResolution {
|
2020-02-06 18:35:10 +01:00
|
|
|
return NewFailResolution(i.circuitKey, i.currentHeight, outcome)
|
|
|
|
}
|
|
|
|
|
|
|
|
// settleRes is a helper function which creates a settle resolution with
|
|
|
|
// the information contained in the invoiceUpdateCtx and the preimage and
|
2020-02-06 18:35:16 +01:00
|
|
|
// the settle resolution result provided.
|
2020-02-06 18:35:10 +01:00
|
|
|
func (i invoiceUpdateCtx) settleRes(preimage lntypes.Preimage,
|
2020-02-06 18:35:16 +01:00
|
|
|
outcome SettleResolutionResult) *HtlcSettleResolution {
|
2020-02-06 18:35:10 +01:00
|
|
|
|
|
|
|
return NewSettleResolution(
|
|
|
|
preimage, i.circuitKey, i.currentHeight, outcome,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// acceptRes is a helper function which creates an accept resolution with
|
2020-02-06 18:35:16 +01:00
|
|
|
// the information contained in the invoiceUpdateCtx and the accept resolution
|
|
|
|
// result provided.
|
2024-05-08 19:17:05 +02:00
|
|
|
func (i invoiceUpdateCtx) acceptRes(
|
|
|
|
outcome acceptResolutionResult) *htlcAcceptResolution {
|
|
|
|
|
2020-02-06 18:35:10 +01:00
|
|
|
return newAcceptResolution(i.circuitKey, outcome)
|
|
|
|
}
|
|
|
|
|
2019-12-03 12:22:31 +01:00
|
|
|
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
|
2024-05-08 19:17:05 +02:00
|
|
|
// settlement logic. It returns a HTLC resolution that indicates what the
|
2020-02-06 18:35:10 +01:00
|
|
|
// outcome of the update was.
|
2022-11-30 12:00:37 +01:00
|
|
|
func updateInvoice(ctx *invoiceUpdateCtx, inv *Invoice) (
|
|
|
|
*InvoiceUpdateDesc, HtlcResolution, error) {
|
2019-12-03 12:22:31 +01:00
|
|
|
|
|
|
|
// Don't update the invoice when this is a replayed htlc.
|
|
|
|
htlc, ok := inv.Htlcs[ctx.circuitKey]
|
|
|
|
if ok {
|
|
|
|
switch htlc.State {
|
2022-11-30 12:00:37 +01:00
|
|
|
case HtlcStateCanceled:
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultReplayToCanceled), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
case HtlcStateAccepted:
|
2020-02-06 18:35:16 +01:00
|
|
|
return nil, ctx.acceptRes(resultReplayToAccepted), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
case HtlcStateSettled:
|
2022-05-11 19:47:41 +02:00
|
|
|
pre := inv.Terms.PaymentPreimage
|
|
|
|
|
|
|
|
// Terms.PaymentPreimage will be nil for AMP invoices.
|
2024-05-08 19:17:05 +02:00
|
|
|
// Set it to the HTLCs AMP Preimage instead.
|
2022-05-11 19:47:41 +02:00
|
|
|
if pre == nil {
|
|
|
|
pre = htlc.AMP.Preimage
|
|
|
|
}
|
|
|
|
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.settleRes(
|
2022-05-11 19:47:41 +02:00
|
|
|
*pre,
|
2020-02-06 18:35:10 +01:00
|
|
|
ResultReplayToSettled,
|
|
|
|
), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
|
|
|
|
default:
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, nil, errors.New("unknown htlc state")
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-07 04:20:46 +01:00
|
|
|
// If no MPP payload was provided, then we expect this to be a keysend,
|
|
|
|
// or a payment to an invoice created before we started to require the
|
|
|
|
// MPP payload.
|
2024-05-05 14:48:50 +02:00
|
|
|
if ctx.mpp == nil && ctx.pathID == nil {
|
2019-09-03 12:23:39 +02:00
|
|
|
return updateLegacy(ctx, inv)
|
|
|
|
}
|
|
|
|
|
|
|
|
return updateMpp(ctx, inv)
|
|
|
|
}
|
|
|
|
|
|
|
|
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
|
|
|
|
// settlement logic for mpp payments.
|
2022-11-30 12:00:37 +01:00
|
|
|
func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc,
|
2020-02-06 18:35:10 +01:00
|
|
|
HtlcResolution, error) {
|
2019-09-03 12:23:39 +02:00
|
|
|
|
2021-05-06 18:17:58 +02:00
|
|
|
// Reject HTLCs to AMP invoices if they are missing an AMP payload, and
|
|
|
|
// HTLCs to MPP invoices if they have an AMP payload.
|
|
|
|
switch {
|
|
|
|
case inv.Terms.Features.RequiresFeature(lnwire.AMPRequired) &&
|
|
|
|
ctx.amp == nil:
|
|
|
|
|
|
|
|
return nil, ctx.failRes(ResultHtlcInvoiceTypeMismatch), nil
|
|
|
|
|
|
|
|
case !inv.Terms.Features.RequiresFeature(lnwire.AMPRequired) &&
|
|
|
|
ctx.amp != nil:
|
|
|
|
|
|
|
|
return nil, ctx.failRes(ResultHtlcInvoiceTypeMismatch), nil
|
|
|
|
}
|
|
|
|
|
2021-03-03 18:59:29 +01:00
|
|
|
setID := ctx.setID()
|
|
|
|
|
2024-05-05 14:48:50 +02:00
|
|
|
var (
|
|
|
|
totalAmt = ctx.totalAmtMsat
|
|
|
|
paymentAddr []byte
|
|
|
|
)
|
|
|
|
// If an MPP record is present, then the payment address and total
|
|
|
|
// payment amount is extracted from it. Otherwise, the pathID is used
|
|
|
|
// to extract the payment address.
|
|
|
|
if ctx.mpp != nil {
|
|
|
|
totalAmt = ctx.mpp.TotalMsat()
|
|
|
|
payAddr := ctx.mpp.PaymentAddr()
|
|
|
|
paymentAddr = payAddr[:]
|
|
|
|
} else {
|
|
|
|
paymentAddr = ctx.pathID[:]
|
|
|
|
}
|
|
|
|
|
2024-05-08 19:17:05 +02:00
|
|
|
// For storage, we don't really care where the custom records came from.
|
|
|
|
// So we merge them together and store them in the same field.
|
|
|
|
customRecords := lnwire.CustomRecords(
|
|
|
|
ctx.customRecords,
|
|
|
|
).MergedCopy(ctx.wireCustomRecords)
|
|
|
|
|
2019-09-03 12:23:39 +02:00
|
|
|
// Start building the accept descriptor.
|
2022-11-30 12:00:37 +01:00
|
|
|
acceptDesc := &HtlcAcceptDesc{
|
2019-09-03 12:23:39 +02:00
|
|
|
Amt: ctx.amtPaid,
|
|
|
|
Expiry: ctx.expiry,
|
|
|
|
AcceptHeight: ctx.currentHeight,
|
2024-05-05 14:48:50 +02:00
|
|
|
MppTotalAmt: totalAmt,
|
2024-05-08 19:17:05 +02:00
|
|
|
CustomRecords: record.CustomSet(customRecords),
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 03:52:01 +01:00
|
|
|
if ctx.amp != nil {
|
2022-11-30 12:00:37 +01:00
|
|
|
acceptDesc.AMP = &InvoiceHtlcAMPData{
|
2021-03-25 03:52:01 +01:00
|
|
|
Record: *ctx.amp,
|
|
|
|
Hash: ctx.hash,
|
|
|
|
Preimage: nil,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 12:23:39 +02:00
|
|
|
// Only accept payments to open invoices. This behaviour differs from
|
|
|
|
// non-mpp payments that are accepted even after the invoice is settled.
|
|
|
|
// Because non-mpp payments don't have a payment address, this is needed
|
|
|
|
// to thwart probing.
|
2022-11-30 12:00:37 +01:00
|
|
|
if inv.State != ContractOpen {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultInvoiceNotOpen), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check the payment address that authorizes the payment.
|
2024-05-05 14:48:50 +02:00
|
|
|
if !bytes.Equal(paymentAddr, inv.Terms.PaymentAddr[:]) {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultAddressMismatch), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Don't accept zero-valued sets.
|
2024-05-05 14:48:50 +02:00
|
|
|
if totalAmt == 0 {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the total amt of the htlc set is high enough. In case this
|
|
|
|
// is a zero-valued invoice, it will always be enough.
|
2024-05-05 14:48:50 +02:00
|
|
|
if totalAmt < inv.Terms.Value {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
htlcSet := inv.HTLCSet(setID, HtlcStateAccepted)
|
2021-03-25 03:52:01 +01:00
|
|
|
|
2024-05-08 19:17:05 +02:00
|
|
|
// Check whether total amt matches other HTLCs in the set.
|
2019-09-03 12:23:39 +02:00
|
|
|
var newSetTotal lnwire.MilliSatoshi
|
2021-03-25 03:52:01 +01:00
|
|
|
for _, htlc := range htlcSet {
|
2024-05-05 14:48:50 +02:00
|
|
|
if totalAmt != htlc.MppTotalAmt {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultHtlcSetTotalMismatch), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
newSetTotal += htlc.Amt
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add amount of new htlc.
|
|
|
|
newSetTotal += ctx.amtPaid
|
|
|
|
|
|
|
|
// The invoice is still open. Check the expiry.
|
|
|
|
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultExpiryTooSoon), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultExpiryTooSoon), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
if setID != nil && *setID == BlankPayAddr {
|
2022-04-14 22:07:06 +02:00
|
|
|
return nil, ctx.failRes(ResultAmpError), nil
|
|
|
|
}
|
|
|
|
|
2019-09-03 12:23:39 +02:00
|
|
|
// Record HTLC in the invoice database.
|
2022-11-30 12:00:37 +01:00
|
|
|
newHtlcs := map[CircuitKey]*HtlcAcceptDesc{
|
2019-09-03 12:23:39 +02:00
|
|
|
ctx.circuitKey: acceptDesc,
|
|
|
|
}
|
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
update := InvoiceUpdateDesc{
|
2023-02-02 12:43:01 +01:00
|
|
|
UpdateType: AddHTLCsUpdate,
|
|
|
|
AddHtlcs: newHtlcs,
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the invoice cannot be settled yet, only record the htlc.
|
2024-05-05 14:48:50 +02:00
|
|
|
setComplete := newSetTotal >= totalAmt
|
2019-09-03 12:23:39 +02:00
|
|
|
if !setComplete {
|
2020-02-06 18:35:16 +01:00
|
|
|
return &update, ctx.acceptRes(resultPartialAccepted), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
2024-05-08 19:17:05 +02:00
|
|
|
// Check to see if we can settle or this is a hold invoice, and
|
2019-09-03 12:23:39 +02:00
|
|
|
// we need to wait for the preimage.
|
2020-04-08 13:47:10 +02:00
|
|
|
if inv.HodlInvoice {
|
2022-11-30 12:00:37 +01:00
|
|
|
update.State = &InvoiceStateUpdateDesc{
|
|
|
|
NewState: ContractAccepted,
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
2020-02-06 18:35:16 +01:00
|
|
|
return &update, ctx.acceptRes(resultAccepted), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 03:52:01 +01:00
|
|
|
var (
|
2022-11-30 12:00:37 +01:00
|
|
|
htlcPreimages map[CircuitKey]lntypes.Preimage
|
2021-03-25 03:52:01 +01:00
|
|
|
htlcPreimage lntypes.Preimage
|
|
|
|
)
|
|
|
|
if ctx.amp != nil {
|
|
|
|
var failRes *HtlcFailResolution
|
|
|
|
htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
|
|
|
|
if failRes != nil {
|
2023-02-01 16:49:53 +01:00
|
|
|
update.UpdateType = CancelInvoiceUpdate
|
2022-11-30 12:00:37 +01:00
|
|
|
update.State = &InvoiceStateUpdateDesc{
|
|
|
|
NewState: ContractCanceled,
|
2021-03-25 03:52:01 +01:00
|
|
|
SetID: setID,
|
|
|
|
}
|
|
|
|
return &update, failRes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The preimage for _this_ HTLC will be the one with context's
|
|
|
|
// circuit key.
|
|
|
|
htlcPreimage = htlcPreimages[ctx.circuitKey]
|
|
|
|
} else {
|
|
|
|
htlcPreimage = *inv.Terms.PaymentPreimage
|
|
|
|
}
|
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
update.State = &InvoiceStateUpdateDesc{
|
|
|
|
NewState: ContractSettled,
|
2021-03-25 03:52:01 +01:00
|
|
|
Preimage: inv.Terms.PaymentPreimage,
|
|
|
|
HTLCPreimages: htlcPreimages,
|
|
|
|
SetID: setID,
|
|
|
|
}
|
|
|
|
|
|
|
|
return &update, ctx.settleRes(htlcPreimage, ResultSettled), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTLCSet is a map of CircuitKey to InvoiceHTLC.
|
2022-11-30 12:00:37 +01:00
|
|
|
type HTLCSet = map[CircuitKey]*InvoiceHTLC
|
2021-03-25 03:52:01 +01:00
|
|
|
|
|
|
|
// HTLCPreimages is a map of CircuitKey to preimage.
|
2022-11-30 12:00:37 +01:00
|
|
|
type HTLCPreimages = map[CircuitKey]lntypes.Preimage
|
2021-03-25 03:52:01 +01:00
|
|
|
|
|
|
|
// reconstructAMPPreimages reconstructs the root seed for an AMP HTLC set and
|
|
|
|
// verifies that all derived child hashes match the payment hashes of the HTLCs
|
|
|
|
// in the set. This method is meant to be called after receiving the full amount
|
|
|
|
// committed to via mpp_total_msat. This method will return a fail resolution if
|
2022-01-13 17:29:43 +01:00
|
|
|
// any of the child hashes fail to match their corresponding HTLCs.
|
2021-03-25 03:52:01 +01:00
|
|
|
func reconstructAMPPreimages(ctx *invoiceUpdateCtx,
|
|
|
|
htlcSet HTLCSet) (HTLCPreimages, *HtlcFailResolution) {
|
|
|
|
|
|
|
|
// Create a slice containing all the child descriptors to be used for
|
|
|
|
// reconstruction. This should include all HTLCs currently in the HTLC
|
|
|
|
// set, plus the incoming HTLC.
|
|
|
|
childDescs := make([]amp.ChildDesc, 0, 1+len(htlcSet))
|
|
|
|
|
|
|
|
// Add the new HTLC's child descriptor at index 0.
|
|
|
|
childDescs = append(childDescs, amp.ChildDesc{
|
|
|
|
Share: ctx.amp.RootShare(),
|
|
|
|
Index: ctx.amp.ChildIndex(),
|
|
|
|
})
|
|
|
|
|
|
|
|
// Next, construct an index mapping the position in childDescs to a
|
|
|
|
// circuit key for all preexisting HTLCs.
|
2022-11-30 12:00:37 +01:00
|
|
|
indexToCircuitKey := make(map[int]CircuitKey)
|
2021-03-25 03:52:01 +01:00
|
|
|
|
|
|
|
// Add the child descriptor for each HTLC in the HTLC set, recording
|
|
|
|
// it's position within the slice.
|
|
|
|
var htlcSetIndex int
|
|
|
|
for circuitKey, htlc := range htlcSet {
|
|
|
|
childDescs = append(childDescs, amp.ChildDesc{
|
|
|
|
Share: htlc.AMP.Record.RootShare(),
|
|
|
|
Index: htlc.AMP.Record.ChildIndex(),
|
|
|
|
})
|
|
|
|
indexToCircuitKey[htlcSetIndex] = circuitKey
|
|
|
|
htlcSetIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Using the child descriptors, reconstruct the root seed and derive the
|
|
|
|
// child hash/preimage pairs for each of the HTLCs.
|
|
|
|
children := amp.ReconstructChildren(childDescs...)
|
|
|
|
|
|
|
|
// Validate that the derived child preimages match the hash of each
|
|
|
|
// HTLC's respective hash.
|
|
|
|
if ctx.hash != children[0].Hash {
|
|
|
|
return nil, ctx.failRes(ResultAmpReconstruction)
|
|
|
|
}
|
|
|
|
for idx, child := range children[1:] {
|
|
|
|
circuitKey := indexToCircuitKey[idx]
|
|
|
|
htlc := htlcSet[circuitKey]
|
|
|
|
if htlc.AMP.Hash != child.Hash {
|
|
|
|
return nil, ctx.failRes(ResultAmpReconstruction)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, construct the map of learned preimages indexed by circuit
|
|
|
|
// key, so that they can be persisted along with each HTLC when updating
|
|
|
|
// the invoice.
|
2022-11-30 12:00:37 +01:00
|
|
|
htlcPreimages := make(map[CircuitKey]lntypes.Preimage)
|
2021-03-25 03:52:01 +01:00
|
|
|
htlcPreimages[ctx.circuitKey] = children[0].Preimage
|
|
|
|
for idx, child := range children[1:] {
|
|
|
|
circuitKey := indexToCircuitKey[idx]
|
|
|
|
htlcPreimages[circuitKey] = child.Preimage
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 03:52:01 +01:00
|
|
|
return htlcPreimages, nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
|
|
|
|
// settlement logic for legacy payments.
|
2020-11-07 04:20:46 +01:00
|
|
|
//
|
|
|
|
// NOTE: This function is only kept in place in order to be able to handle key
|
|
|
|
// send payments and any invoices we created in the past that are valid and
|
|
|
|
// still had the optional mpp bit set.
|
2020-02-06 18:35:10 +01:00
|
|
|
func updateLegacy(ctx *invoiceUpdateCtx,
|
2022-11-30 12:00:37 +01:00
|
|
|
inv *Invoice) (*InvoiceUpdateDesc, HtlcResolution, error) {
|
2019-09-03 12:23:39 +02:00
|
|
|
|
|
|
|
// If the invoice is already canceled, there is no further
|
|
|
|
// checking to do.
|
2022-11-30 12:00:37 +01:00
|
|
|
if inv.State == ContractCanceled {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultInvoiceAlreadyCanceled), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If an invoice amount is specified, check that enough is paid. Also
|
|
|
|
// check this for duplicate payments if the invoice is already settled
|
2019-12-03 20:41:46 +01:00
|
|
|
// or accepted. In case this is a zero-valued invoice, it will always be
|
|
|
|
// enough.
|
|
|
|
if ctx.amtPaid < inv.Terms.Value {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultAmountTooLow), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
2020-11-07 04:20:46 +01:00
|
|
|
// If the invoice had the required feature bit set at this point, then
|
|
|
|
// if we're in this method it means that the remote party didn't supply
|
|
|
|
// the expected payload. However if this is a keysend payment, then
|
|
|
|
// we'll permit it to pass.
|
|
|
|
_, isKeySend := ctx.customRecords[record.KeySendType]
|
|
|
|
invoiceFeatures := inv.Terms.Features
|
|
|
|
paymentAddrRequired := invoiceFeatures.RequiresFeature(
|
|
|
|
lnwire.PaymentAddrRequired,
|
|
|
|
)
|
|
|
|
if !isKeySend && paymentAddrRequired {
|
|
|
|
log.Warnf("Payment to pay_hash=%v doesn't include MPP "+
|
|
|
|
"payload, rejecting", ctx.hash)
|
|
|
|
return nil, ctx.failRes(ResultAddressMismatch), nil
|
|
|
|
}
|
2019-09-03 12:23:39 +02:00
|
|
|
|
|
|
|
// Don't allow settling the invoice with an old style
|
|
|
|
// htlc if we are already in the process of gathering an
|
|
|
|
// mpp set.
|
2022-11-30 12:00:37 +01:00
|
|
|
for _, htlc := range inv.HTLCSet(nil, HtlcStateAccepted) {
|
2021-03-03 18:55:56 +01:00
|
|
|
if htlc.MppTotalAmt > 0 {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultMppInProgress), nil
|
2019-09-03 12:23:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 12:22:31 +01:00
|
|
|
// The invoice is still open. Check the expiry.
|
|
|
|
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultExpiryTooSoon), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
2020-02-06 18:35:10 +01:00
|
|
|
return nil, ctx.failRes(ResultExpiryTooSoon), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
2024-05-08 19:17:05 +02:00
|
|
|
// For storage, we don't really care where the custom records came from.
|
|
|
|
// So we merge them together and store them in the same field.
|
|
|
|
customRecords := lnwire.CustomRecords(
|
|
|
|
ctx.customRecords,
|
|
|
|
).MergedCopy(ctx.wireCustomRecords)
|
|
|
|
|
2019-12-03 12:22:31 +01:00
|
|
|
// Record HTLC in the invoice database.
|
2022-11-30 12:00:37 +01:00
|
|
|
newHtlcs := map[CircuitKey]*HtlcAcceptDesc{
|
2019-12-03 12:22:31 +01:00
|
|
|
ctx.circuitKey: {
|
2019-11-19 13:33:05 +01:00
|
|
|
Amt: ctx.amtPaid,
|
|
|
|
Expiry: ctx.expiry,
|
|
|
|
AcceptHeight: ctx.currentHeight,
|
2024-05-08 19:17:05 +02:00
|
|
|
CustomRecords: record.CustomSet(customRecords),
|
2019-12-03 12:22:31 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
update := InvoiceUpdateDesc{
|
2023-02-02 12:43:01 +01:00
|
|
|
AddHtlcs: newHtlcs,
|
|
|
|
UpdateType: AddHTLCsUpdate,
|
2019-11-27 14:19:15 +01:00
|
|
|
}
|
2019-12-03 12:22:31 +01:00
|
|
|
|
|
|
|
// Don't update invoice state if we are accepting a duplicate payment.
|
|
|
|
// We do accept or settle the HTLC.
|
|
|
|
switch inv.State {
|
2022-11-30 12:00:37 +01:00
|
|
|
case ContractAccepted:
|
2020-02-06 18:35:16 +01:00
|
|
|
return &update, ctx.acceptRes(resultDuplicateToAccepted), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
case ContractSettled:
|
2020-02-06 18:35:10 +01:00
|
|
|
return &update, ctx.settleRes(
|
2020-04-08 13:47:10 +02:00
|
|
|
*inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
|
2020-02-06 18:35:10 +01:00
|
|
|
), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check to see if we can settle or this is an hold invoice and we need
|
|
|
|
// to wait for the preimage.
|
2020-04-08 13:47:10 +02:00
|
|
|
if inv.HodlInvoice {
|
2022-11-30 12:00:37 +01:00
|
|
|
update.State = &InvoiceStateUpdateDesc{
|
|
|
|
NewState: ContractAccepted,
|
2019-11-27 13:20:14 +01:00
|
|
|
}
|
2020-02-06 18:35:10 +01:00
|
|
|
|
2020-02-06 18:35:16 +01:00
|
|
|
return &update, ctx.acceptRes(resultAccepted), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|
|
|
|
|
2022-11-30 12:00:37 +01:00
|
|
|
update.State = &InvoiceStateUpdateDesc{
|
|
|
|
NewState: ContractSettled,
|
2019-11-27 13:20:14 +01:00
|
|
|
Preimage: inv.Terms.PaymentPreimage,
|
|
|
|
}
|
2019-12-03 12:22:31 +01:00
|
|
|
|
2020-02-06 18:35:10 +01:00
|
|
|
return &update, ctx.settleRes(
|
2020-04-08 13:47:10 +02:00
|
|
|
*inv.Terms.PaymentPreimage, ResultSettled,
|
2020-02-06 18:35:10 +01:00
|
|
|
), nil
|
2019-12-03 12:22:31 +01:00
|
|
|
}
|