github.com/decred/dcrlnd@v0.7.6/invoices/update.go (about)

     1  package invoices
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/decred/dcrlnd/amp"
     7  	"github.com/decred/dcrlnd/channeldb"
     8  	"github.com/decred/dcrlnd/lntypes"
     9  	"github.com/decred/dcrlnd/lnwire"
    10  	"github.com/decred/dcrlnd/record"
    11  )
    12  
    13  // invoiceUpdateCtx is an object that describes the context for the invoice
    14  // update to be carried out.
    15  type invoiceUpdateCtx struct {
    16  	hash                 lntypes.Hash
    17  	circuitKey           channeldb.CircuitKey
    18  	amtPaid              lnwire.MilliAtom
    19  	expiry               uint32
    20  	currentHeight        int32
    21  	finalCltvRejectDelta int32
    22  	customRecords        record.CustomSet
    23  	mpp                  *record.MPP
    24  	amp                  *record.AMP
    25  }
    26  
    27  // invoiceRef returns an identifier that can be used to lookup or update the
    28  // invoice this HTLC is targeting.
    29  func (i *invoiceUpdateCtx) invoiceRef() channeldb.InvoiceRef {
    30  	switch {
    31  	case i.amp != nil && i.mpp != nil:
    32  		payAddr := i.mpp.PaymentAddr()
    33  		return channeldb.InvoiceRefByAddr(payAddr)
    34  	case i.mpp != nil:
    35  		payAddr := i.mpp.PaymentAddr()
    36  		return channeldb.InvoiceRefByHashAndAddr(i.hash, payAddr)
    37  	default:
    38  		return channeldb.InvoiceRefByHash(i.hash)
    39  	}
    40  }
    41  
    42  // setID returns an identifier that identifies other possible HTLCs that this
    43  // particular one is related to. If nil is returned this means the HTLC is an
    44  // MPP or legacy payment, otherwise the HTLC belongs AMP payment.
    45  func (i invoiceUpdateCtx) setID() *[32]byte {
    46  	if i.amp != nil {
    47  		setID := i.amp.SetID()
    48  		return &setID
    49  	}
    50  	return nil
    51  }
    52  
    53  // log logs a message specific to this update context.
    54  func (i *invoiceUpdateCtx) log(s string) {
    55  	log.Debugf("Invoice%v: %v, amt=%v, expiry=%v, circuit=%v, mpp=%v, "+
    56  		"amp=%v", i.invoiceRef(), s, i.amtPaid, i.expiry, i.circuitKey,
    57  		i.mpp, i.amp)
    58  }
    59  
    60  // failRes is a helper function which creates a failure resolution with
    61  // the information contained in the invoiceUpdateCtx and the fail resolution
    62  // result provided.
    63  func (i invoiceUpdateCtx) failRes(outcome FailResolutionResult) *HtlcFailResolution {
    64  	return NewFailResolution(i.circuitKey, i.currentHeight, outcome)
    65  }
    66  
    67  // settleRes is a helper function which creates a settle resolution with
    68  // the information contained in the invoiceUpdateCtx and the preimage and
    69  // the settle resolution result provided.
    70  func (i invoiceUpdateCtx) settleRes(preimage lntypes.Preimage,
    71  	outcome SettleResolutionResult) *HtlcSettleResolution {
    72  
    73  	return NewSettleResolution(
    74  		preimage, i.circuitKey, i.currentHeight, outcome,
    75  	)
    76  }
    77  
    78  // acceptRes is a helper function which creates an accept resolution with
    79  // the information contained in the invoiceUpdateCtx and the accept resolution
    80  // result provided.
    81  func (i invoiceUpdateCtx) acceptRes(outcome acceptResolutionResult) *htlcAcceptResolution {
    82  	return newAcceptResolution(i.circuitKey, outcome)
    83  }
    84  
    85  // updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
    86  // settlement logic. It returns a hltc resolution that indicates what the
    87  // outcome of the update was.
    88  func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
    89  	*channeldb.InvoiceUpdateDesc, HtlcResolution, error) {
    90  
    91  	// Don't update the invoice when this is a replayed htlc.
    92  	htlc, ok := inv.Htlcs[ctx.circuitKey]
    93  	if ok {
    94  		switch htlc.State {
    95  		case channeldb.HtlcStateCanceled:
    96  			return nil, ctx.failRes(ResultReplayToCanceled), nil
    97  
    98  		case channeldb.HtlcStateAccepted:
    99  			return nil, ctx.acceptRes(resultReplayToAccepted), nil
   100  
   101  		case channeldb.HtlcStateSettled:
   102  			return nil, ctx.settleRes(
   103  				*inv.Terms.PaymentPreimage,
   104  				ResultReplayToSettled,
   105  			), nil
   106  
   107  		default:
   108  			return nil, nil, errors.New("unknown htlc state")
   109  		}
   110  	}
   111  
   112  	// If no MPP payload was provided, then we expect this to be a keysend,
   113  	// or a payment to an invoice created before we started to require the
   114  	// MPP payload.
   115  	if ctx.mpp == nil {
   116  		return updateLegacy(ctx, inv)
   117  	}
   118  
   119  	return updateMpp(ctx, inv)
   120  }
   121  
   122  // updateMpp is a callback for DB.UpdateInvoice that contains the invoice
   123  // settlement logic for mpp payments.
   124  func updateMpp(ctx *invoiceUpdateCtx,
   125  	inv *channeldb.Invoice) (*channeldb.InvoiceUpdateDesc,
   126  	HtlcResolution, error) {
   127  
   128  	// Reject HTLCs to AMP invoices if they are missing an AMP payload, and
   129  	// HTLCs to MPP invoices if they have an AMP payload.
   130  	switch {
   131  
   132  	case inv.Terms.Features.RequiresFeature(lnwire.AMPRequired) &&
   133  		ctx.amp == nil:
   134  
   135  		return nil, ctx.failRes(ResultHtlcInvoiceTypeMismatch), nil
   136  
   137  	case !inv.Terms.Features.RequiresFeature(lnwire.AMPRequired) &&
   138  		ctx.amp != nil:
   139  
   140  		return nil, ctx.failRes(ResultHtlcInvoiceTypeMismatch), nil
   141  	}
   142  
   143  	setID := ctx.setID()
   144  
   145  	// Start building the accept descriptor.
   146  	acceptDesc := &channeldb.HtlcAcceptDesc{
   147  		Amt:           ctx.amtPaid,
   148  		Expiry:        ctx.expiry,
   149  		AcceptHeight:  ctx.currentHeight,
   150  		MppTotalAmt:   ctx.mpp.TotalMAtoms(),
   151  		CustomRecords: ctx.customRecords,
   152  	}
   153  
   154  	if ctx.amp != nil {
   155  		acceptDesc.AMP = &channeldb.InvoiceHtlcAMPData{
   156  			Record:   *ctx.amp,
   157  			Hash:     ctx.hash,
   158  			Preimage: nil,
   159  		}
   160  	}
   161  
   162  	// Only accept payments to open invoices. This behaviour differs from
   163  	// non-mpp payments that are accepted even after the invoice is settled.
   164  	// Because non-mpp payments don't have a payment address, this is needed
   165  	// to thwart probing.
   166  	if inv.State != channeldb.ContractOpen {
   167  		return nil, ctx.failRes(ResultInvoiceNotOpen), nil
   168  	}
   169  
   170  	// Check the payment address that authorizes the payment.
   171  	if ctx.mpp.PaymentAddr() != inv.Terms.PaymentAddr {
   172  		return nil, ctx.failRes(ResultAddressMismatch), nil
   173  	}
   174  
   175  	// Don't accept zero-valued sets.
   176  	if ctx.mpp.TotalMAtoms() == 0 {
   177  		return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
   178  	}
   179  
   180  	// Check that the total amt of the htlc set is high enough. In case this
   181  	// is a zero-valued invoice, it will always be enough.
   182  	if ctx.mpp.TotalMAtoms() < inv.Terms.Value {
   183  		return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
   184  	}
   185  
   186  	htlcSet := inv.HTLCSet(setID, channeldb.HtlcStateAccepted)
   187  
   188  	// Check whether total amt matches other htlcs in the set.
   189  	var newSetTotal lnwire.MilliAtom
   190  	for _, htlc := range htlcSet {
   191  		// Only consider accepted mpp htlcs. It is possible that there
   192  		// are htlcs registered in the invoice database that previously
   193  		// timed out and are in the canceled state now.
   194  		if htlc.State != channeldb.HtlcStateAccepted {
   195  			continue
   196  		}
   197  
   198  		if ctx.mpp.TotalMAtoms() != htlc.MppTotalAmt {
   199  			return nil, ctx.failRes(ResultHtlcSetTotalMismatch), nil
   200  		}
   201  
   202  		newSetTotal += htlc.Amt
   203  	}
   204  
   205  	// Add amount of new htlc.
   206  	newSetTotal += ctx.amtPaid
   207  
   208  	// Make sure the communicated set total isn't overpaid.
   209  	if newSetTotal > ctx.mpp.TotalMAtoms() {
   210  		return nil, ctx.failRes(ResultHtlcSetOverpayment), nil
   211  	}
   212  
   213  	// The invoice is still open. Check the expiry.
   214  	if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
   215  		return nil, ctx.failRes(ResultExpiryTooSoon), nil
   216  	}
   217  
   218  	if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
   219  		return nil, ctx.failRes(ResultExpiryTooSoon), nil
   220  	}
   221  
   222  	// Record HTLC in the invoice database.
   223  	newHtlcs := map[channeldb.CircuitKey]*channeldb.HtlcAcceptDesc{
   224  		ctx.circuitKey: acceptDesc,
   225  	}
   226  
   227  	update := channeldb.InvoiceUpdateDesc{
   228  		AddHtlcs: newHtlcs,
   229  	}
   230  
   231  	// If the invoice cannot be settled yet, only record the htlc.
   232  	setComplete := newSetTotal == ctx.mpp.TotalMAtoms()
   233  	if !setComplete {
   234  		return &update, ctx.acceptRes(resultPartialAccepted), nil
   235  	}
   236  
   237  	// Check to see if we can settle or this is an hold invoice and
   238  	// we need to wait for the preimage.
   239  	if inv.HodlInvoice {
   240  		update.State = &channeldb.InvoiceStateUpdateDesc{
   241  			NewState: channeldb.ContractAccepted,
   242  			SetID:    setID,
   243  		}
   244  		return &update, ctx.acceptRes(resultAccepted), nil
   245  	}
   246  
   247  	var (
   248  		htlcPreimages map[channeldb.CircuitKey]lntypes.Preimage
   249  		htlcPreimage  lntypes.Preimage
   250  	)
   251  	if ctx.amp != nil {
   252  		var failRes *HtlcFailResolution
   253  		htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
   254  		if failRes != nil {
   255  			update.State = &channeldb.InvoiceStateUpdateDesc{
   256  				NewState: channeldb.ContractCanceled,
   257  				SetID:    setID,
   258  			}
   259  			return &update, failRes, nil
   260  		}
   261  
   262  		// The preimage for _this_ HTLC will be the one with context's
   263  		// circuit key.
   264  		htlcPreimage = htlcPreimages[ctx.circuitKey]
   265  	} else {
   266  		htlcPreimage = *inv.Terms.PaymentPreimage
   267  	}
   268  
   269  	update.State = &channeldb.InvoiceStateUpdateDesc{
   270  		NewState:      channeldb.ContractSettled,
   271  		Preimage:      inv.Terms.PaymentPreimage,
   272  		HTLCPreimages: htlcPreimages,
   273  		SetID:         setID,
   274  	}
   275  
   276  	return &update, ctx.settleRes(htlcPreimage, ResultSettled), nil
   277  }
   278  
   279  // HTLCSet is a map of CircuitKey to InvoiceHTLC.
   280  type HTLCSet = map[channeldb.CircuitKey]*channeldb.InvoiceHTLC
   281  
   282  // HTLCPreimages is a map of CircuitKey to preimage.
   283  type HTLCPreimages = map[channeldb.CircuitKey]lntypes.Preimage
   284  
   285  // reconstructAMPPreimages reconstructs the root seed for an AMP HTLC set and
   286  // verifies that all derived child hashes match the payment hashes of the HTLCs
   287  // in the set. This method is meant to be called after receiving the full amount
   288  // committed to via mpp_total_msat. This method will return a fail resolution if
   289  // any of the child hashes fail to matche theire corresponding HTLCs.
   290  func reconstructAMPPreimages(ctx *invoiceUpdateCtx,
   291  	htlcSet HTLCSet) (HTLCPreimages, *HtlcFailResolution) {
   292  
   293  	// Create a slice containing all the child descriptors to be used for
   294  	// reconstruction. This should include all HTLCs currently in the HTLC
   295  	// set, plus the incoming HTLC.
   296  	childDescs := make([]amp.ChildDesc, 0, 1+len(htlcSet))
   297  
   298  	// Add the new HTLC's child descriptor at index 0.
   299  	childDescs = append(childDescs, amp.ChildDesc{
   300  		Share: ctx.amp.RootShare(),
   301  		Index: ctx.amp.ChildIndex(),
   302  	})
   303  
   304  	// Next, construct an index mapping the position in childDescs to a
   305  	// circuit key for all preexisting HTLCs.
   306  	indexToCircuitKey := make(map[int]channeldb.CircuitKey)
   307  
   308  	// Add the child descriptor for each HTLC in the HTLC set, recording
   309  	// it's position within the slice.
   310  	var htlcSetIndex int
   311  	for circuitKey, htlc := range htlcSet {
   312  		childDescs = append(childDescs, amp.ChildDesc{
   313  			Share: htlc.AMP.Record.RootShare(),
   314  			Index: htlc.AMP.Record.ChildIndex(),
   315  		})
   316  		indexToCircuitKey[htlcSetIndex] = circuitKey
   317  		htlcSetIndex++
   318  	}
   319  
   320  	// Using the child descriptors, reconstruct the root seed and derive the
   321  	// child hash/preimage pairs for each of the HTLCs.
   322  	children := amp.ReconstructChildren(childDescs...)
   323  
   324  	// Validate that the derived child preimages match the hash of each
   325  	// HTLC's respective hash.
   326  	if ctx.hash != children[0].Hash {
   327  		return nil, ctx.failRes(ResultAmpReconstruction)
   328  	}
   329  	for idx, child := range children[1:] {
   330  		circuitKey := indexToCircuitKey[idx]
   331  		htlc := htlcSet[circuitKey]
   332  		if htlc.AMP.Hash != child.Hash {
   333  			return nil, ctx.failRes(ResultAmpReconstruction)
   334  		}
   335  	}
   336  
   337  	// Finally, construct the map of learned preimages indexed by circuit
   338  	// key, so that they can be persisted along with each HTLC when updating
   339  	// the invoice.
   340  	htlcPreimages := make(map[channeldb.CircuitKey]lntypes.Preimage)
   341  	htlcPreimages[ctx.circuitKey] = children[0].Preimage
   342  	for idx, child := range children[1:] {
   343  		circuitKey := indexToCircuitKey[idx]
   344  		htlcPreimages[circuitKey] = child.Preimage
   345  	}
   346  
   347  	return htlcPreimages, nil
   348  }
   349  
   350  // updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
   351  // settlement logic for legacy payments.
   352  //
   353  // NOTE: This function is only kept in place in order to be able to handle key
   354  // send payments and any invoices we created in the past that are valid and
   355  // still had the optional mpp bit set.
   356  func updateLegacy(ctx *invoiceUpdateCtx,
   357  	inv *channeldb.Invoice) (*channeldb.InvoiceUpdateDesc, HtlcResolution, error) {
   358  
   359  	// If the invoice is already canceled, there is no further
   360  	// checking to do.
   361  	if inv.State == channeldb.ContractCanceled {
   362  		return nil, ctx.failRes(ResultInvoiceAlreadyCanceled), nil
   363  	}
   364  
   365  	// If an invoice amount is specified, check that enough is paid. Also
   366  	// check this for duplicate payments if the invoice is already settled
   367  	// or accepted. In case this is a zero-valued invoice, it will always be
   368  	// enough.
   369  	if ctx.amtPaid < inv.Terms.Value {
   370  		return nil, ctx.failRes(ResultAmountTooLow), nil
   371  	}
   372  
   373  	// If the invoice had the required feature bit set at this point, then
   374  	// if we're in this method it means that the remote party didn't supply
   375  	// the expected payload. However if this is a keysend payment, then
   376  	// we'll permit it to pass.
   377  	_, isKeySend := ctx.customRecords[record.KeySendType]
   378  	invoiceFeatures := inv.Terms.Features
   379  	paymentAddrRequired := invoiceFeatures.RequiresFeature(
   380  		lnwire.PaymentAddrRequired,
   381  	)
   382  	if !isKeySend && paymentAddrRequired {
   383  		log.Warnf("Payment to pay_hash=%v doesn't include MPP "+
   384  			"payload, rejecting", ctx.hash)
   385  		return nil, ctx.failRes(ResultAddressMismatch), nil
   386  	}
   387  
   388  	// Don't allow settling the invoice with an old style
   389  	// htlc if we are already in the process of gathering an
   390  	// mpp set.
   391  	for _, htlc := range inv.HTLCSet(nil, channeldb.HtlcStateAccepted) {
   392  		if htlc.MppTotalAmt > 0 {
   393  			return nil, ctx.failRes(ResultMppInProgress), nil
   394  		}
   395  	}
   396  
   397  	// The invoice is still open. Check the expiry.
   398  	if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
   399  		return nil, ctx.failRes(ResultExpiryTooSoon), nil
   400  	}
   401  
   402  	if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
   403  		return nil, ctx.failRes(ResultExpiryTooSoon), nil
   404  	}
   405  
   406  	// Record HTLC in the invoice database.
   407  	newHtlcs := map[channeldb.CircuitKey]*channeldb.HtlcAcceptDesc{
   408  		ctx.circuitKey: {
   409  			Amt:           ctx.amtPaid,
   410  			Expiry:        ctx.expiry,
   411  			AcceptHeight:  ctx.currentHeight,
   412  			CustomRecords: ctx.customRecords,
   413  		},
   414  	}
   415  
   416  	update := channeldb.InvoiceUpdateDesc{
   417  		AddHtlcs: newHtlcs,
   418  	}
   419  
   420  	// Don't update invoice state if we are accepting a duplicate payment.
   421  	// We do accept or settle the HTLC.
   422  	switch inv.State {
   423  	case channeldb.ContractAccepted:
   424  		return &update, ctx.acceptRes(resultDuplicateToAccepted), nil
   425  
   426  	case channeldb.ContractSettled:
   427  		return &update, ctx.settleRes(
   428  			*inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
   429  		), nil
   430  	}
   431  
   432  	// Check to see if we can settle or this is an hold invoice and we need
   433  	// to wait for the preimage.
   434  	if inv.HodlInvoice {
   435  		update.State = &channeldb.InvoiceStateUpdateDesc{
   436  			NewState: channeldb.ContractAccepted,
   437  		}
   438  
   439  		return &update, ctx.acceptRes(resultAccepted), nil
   440  	}
   441  
   442  	update.State = &channeldb.InvoiceStateUpdateDesc{
   443  		NewState: channeldb.ContractSettled,
   444  		Preimage: inv.Terms.PaymentPreimage,
   445  	}
   446  
   447  	return &update, ctx.settleRes(
   448  		*inv.Terms.PaymentPreimage, ResultSettled,
   449  	), nil
   450  }