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 }