github.com/decred/dcrlnd@v0.7.6/contractcourt/htlc_incoming_contest_resolver.go (about) 1 package contractcourt 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 10 "github.com/decred/dcrd/dcrutil/v4" 11 "github.com/decred/dcrlnd/channeldb" 12 "github.com/decred/dcrlnd/htlcswitch/hop" 13 "github.com/decred/dcrlnd/input" 14 "github.com/decred/dcrlnd/invoices" 15 "github.com/decred/dcrlnd/lntypes" 16 "github.com/decred/dcrlnd/lnwallet" 17 ) 18 19 // htlcIncomingContestResolver is a ContractResolver that's able to resolve an 20 // incoming HTLC that is still contested. An HTLC is still contested, if at the 21 // time of commitment broadcast, we don't know of the preimage for it yet, and 22 // it hasn't expired. In this case, we can resolve the HTLC if we learn of the 23 // preimage, otherwise the remote party will sweep it after it expires. 24 // 25 // TODO(roasbeef): just embed the other resolver? 26 type htlcIncomingContestResolver struct { 27 // htlcExpiry is the absolute expiry of this incoming HTLC. We use this 28 // value to determine if we can exit early as if the HTLC times out, 29 // before we learn of the preimage then we can't claim it on chain 30 // successfully. 31 htlcExpiry uint32 32 33 // htlcSuccessResolver is the inner resolver that may be utilized if we 34 // learn of the preimage. 35 *htlcSuccessResolver 36 } 37 38 // newIncomingContestResolver instantiates a new incoming htlc contest resolver. 39 func newIncomingContestResolver( 40 res lnwallet.IncomingHtlcResolution, broadcastHeight uint32, 41 htlc channeldb.HTLC, resCfg ResolverConfig) *htlcIncomingContestResolver { 42 43 success := newSuccessResolver( 44 res, broadcastHeight, htlc, resCfg, 45 ) 46 47 return &htlcIncomingContestResolver{ 48 htlcExpiry: htlc.RefundTimeout, 49 htlcSuccessResolver: success, 50 } 51 } 52 53 // Resolve attempts to resolve this contract. As we don't yet know of the 54 // preimage for the contract, we'll wait for one of two things to happen: 55 // 56 // 1. We learn of the preimage! In this case, we can sweep the HTLC incoming 57 // and ensure that if this was a multi-hop HTLC we are made whole. In this 58 // case, an additional ContractResolver will be returned to finish the 59 // job. 60 // 61 // 2. The HTLC expires. If this happens, then the contract is fully resolved 62 // as we have no remaining actions left at our disposal. 63 // 64 // NOTE: Part of the ContractResolver interface. 65 func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) { 66 // If we're already full resolved, then we don't have anything further 67 // to do. 68 if h.resolved { 69 return nil, nil 70 } 71 72 // First try to parse the payload. If that fails, we can stop resolution 73 // now. 74 payload, err := h.decodePayload() 75 if err != nil { 76 log.Debugf("ChannelArbitrator(%v): cannot decode payload of "+ 77 "htlc %v", h.ChanPoint, h.HtlcPoint()) 78 79 // If we've locked in an htlc with an invalid payload on our 80 // commitment tx, we don't need to resolve it. The other party 81 // will time it out and get their funds back. This situation can 82 // present itself when we crash before processRemoteAdds in the 83 // link has ran. 84 h.resolved = true 85 86 // We write a report to disk that indicates we could not decode 87 // the htlc. 88 resReport := h.report().resolverReport( 89 nil, channeldb.ResolverTypeIncomingHtlc, 90 channeldb.ResolverOutcomeAbandoned, 91 ) 92 return nil, h.PutResolverReport(nil, resReport) 93 } 94 95 // Register for block epochs. After registration, the current height 96 // will be sent on the channel immediately. 97 blockEpochs, err := h.Notifier.RegisterBlockEpochNtfn(nil) 98 if err != nil { 99 return nil, err 100 } 101 defer blockEpochs.Cancel() 102 103 var currentHeight int32 104 select { 105 case newBlock, ok := <-blockEpochs.Epochs: 106 if !ok { 107 return nil, errResolverShuttingDown 108 } 109 currentHeight = newBlock.Height 110 case <-h.quit: 111 return nil, errResolverShuttingDown 112 } 113 114 // We'll first check if this HTLC has been timed out, if so, we can 115 // return now and mark ourselves as resolved. If we're past the point of 116 // expiry of the HTLC, then at this point the sender can sweep it, so 117 // we'll end our lifetime. Here we deliberately forego the chance that 118 // the sender doesn't sweep and we already have or will learn the 119 // preimage. Otherwise the resolver could potentially stay active 120 // indefinitely and the channel will never close properly. 121 if uint32(currentHeight) >= h.htlcExpiry { 122 // TODO(roasbeef): should also somehow check if outgoing is 123 // resolved or not 124 // * may need to hook into the circuit map 125 // * can't timeout before the outgoing has been 126 127 log.Infof("%T(%v): HTLC has timed out (expiry=%v, height=%v), "+ 128 "abandoning", h, h.htlcResolution.ClaimOutpoint, 129 h.htlcExpiry, currentHeight) 130 h.resolved = true 131 132 // Finally, get our report and checkpoint our resolver with a 133 // timeout outcome report. 134 report := h.report().resolverReport( 135 nil, channeldb.ResolverTypeIncomingHtlc, 136 channeldb.ResolverOutcomeTimeout, 137 ) 138 return nil, h.Checkpoint(h, report) 139 } 140 141 // applyPreimage is a helper function that will populate our internal 142 // resolver with the preimage we learn of. This should be called once 143 // the preimage is revealed so the inner resolver can properly complete 144 // its duties. The error return value indicates whether the preimage 145 // was properly applied. 146 applyPreimage := func(preimage lntypes.Preimage) error { 147 // Sanity check to see if this preimage matches our htlc. At 148 // this point it should never happen that it does not match. 149 if !preimage.Matches(h.htlc.RHash) { 150 return errors.New("preimage does not match hash") 151 } 152 153 // Update htlcResolution with the matching preimage. 154 h.htlcResolution.Preimage = preimage 155 156 log.Infof("%T(%v): extracted preimage=%v from beacon!", h, 157 h.htlcResolution.ClaimOutpoint, preimage) 158 159 // If this is our commitment transaction, then we'll need to 160 // populate the witness for the second-level HTLC transaction. 161 if h.htlcResolution.SignedSuccessTx != nil { 162 // Within the witness for the success transaction, the 163 // preimage is the 4th element as it looks like: 164 // 165 // * <sender sig> <recvr sig> <preimage> <witness script> 166 // 167 // We'll populate it within the witness, as since this 168 // was a "contest" resolver, we didn't yet know of the 169 // preimage. 170 newSigScript, err := input.ReplaceReceiverHtlcSpendRedeemPreimage( 171 h.htlcResolution.SignedSuccessTx.TxIn[0].SignatureScript, preimage[:], 172 ) 173 if err != nil { 174 return fmt.Errorf("error replacing preimage in txin: %v", err) 175 } else { 176 h.htlcResolution.SignedSuccessTx.TxIn[0].SignatureScript = newSigScript 177 } 178 } 179 180 return nil 181 } 182 183 // Define a closure to process htlc resolutions either directly or 184 // triggered by future notifications. 185 processHtlcResolution := func(e invoices.HtlcResolution) ( 186 ContractResolver, error) { 187 188 // Take action based on the type of resolution we have 189 // received. 190 switch resolution := e.(type) { 191 192 // If the htlc resolution was a settle, apply the 193 // preimage and return a success resolver. 194 case *invoices.HtlcSettleResolution: 195 err := applyPreimage(resolution.Preimage) 196 if err != nil { 197 return nil, err 198 } 199 200 return h.htlcSuccessResolver, nil 201 202 // If the htlc was failed, mark the htlc as 203 // resolved. 204 case *invoices.HtlcFailResolution: 205 log.Infof("%T(%v): Exit hop HTLC canceled "+ 206 "(expiry=%v, height=%v), abandoning", h, 207 h.htlcResolution.ClaimOutpoint, 208 h.htlcExpiry, currentHeight) 209 210 h.resolved = true 211 212 // Checkpoint our resolver with an abandoned outcome 213 // because we take no further action on this htlc. 214 report := h.report().resolverReport( 215 nil, channeldb.ResolverTypeIncomingHtlc, 216 channeldb.ResolverOutcomeAbandoned, 217 ) 218 return nil, h.Checkpoint(h, report) 219 220 // Error if the resolution type is unknown, we are only 221 // expecting settles and fails. 222 default: 223 return nil, fmt.Errorf("unknown resolution"+ 224 " type: %v", e) 225 } 226 } 227 228 var ( 229 hodlChan chan interface{} 230 witnessUpdates <-chan lntypes.Preimage 231 ) 232 if payload.FwdInfo.NextHop == hop.Exit { 233 // Create a buffered hodl chan to prevent deadlock. 234 hodlChan = make(chan interface{}, 1) 235 236 // Notify registry that we are potentially resolving as an exit 237 // hop on-chain. If this HTLC indeed pays to an existing 238 // invoice, the invoice registry will tell us what to do with 239 // the HTLC. This is identical to HTLC resolution in the link. 240 circuitKey := channeldb.CircuitKey{ 241 ChanID: h.ShortChanID, 242 HtlcID: h.htlc.HtlcIndex, 243 } 244 245 resolution, err := h.Registry.NotifyExitHopHtlc( 246 h.htlc.RHash, h.htlc.Amt, h.htlcExpiry, currentHeight, 247 circuitKey, hodlChan, payload, 248 ) 249 if err != nil { 250 return nil, err 251 } 252 253 defer h.Registry.HodlUnsubscribeAll(hodlChan) 254 255 // Take action based on the resolution we received. If the htlc 256 // was settled, or a htlc for a known invoice failed we can 257 // resolve it directly. If the resolution is nil, the htlc was 258 // neither accepted nor failed, so we cannot take action yet. 259 switch res := resolution.(type) { 260 case *invoices.HtlcFailResolution: 261 // In the case where the htlc failed, but the invoice 262 // was known to the registry, we can directly resolve 263 // the htlc. 264 if res.Outcome != invoices.ResultInvoiceNotFound { 265 return processHtlcResolution(resolution) 266 } 267 268 // If we settled the htlc, we can resolve it. 269 case *invoices.HtlcSettleResolution: 270 return processHtlcResolution(resolution) 271 272 // If the resolution is nil, the htlc was neither settled nor 273 // failed so we cannot take action at present. 274 case nil: 275 276 default: 277 return nil, fmt.Errorf("unknown htlc resolution type: %T", 278 resolution) 279 } 280 } else { 281 // If the HTLC hasn't expired yet, then we may still be able to 282 // claim it if we learn of the pre-image, so we'll subscribe to 283 // the preimage database to see if it turns up, or the HTLC 284 // times out. 285 // 286 // NOTE: This is done BEFORE opportunistically querying the db, 287 // to ensure the preimage can't be delivered between querying 288 // and registering for the preimage subscription. 289 preimageSubscription := h.PreimageDB.SubscribeUpdates() 290 defer preimageSubscription.CancelSubscription() 291 292 // With the epochs and preimage subscriptions initialized, we'll 293 // query to see if we already know the preimage. 294 preimage, ok := h.PreimageDB.LookupPreimage(h.htlc.RHash) 295 if ok { 296 // If we do, then this means we can claim the HTLC! 297 // However, we don't know how to ourselves, so we'll 298 // return our inner resolver which has the knowledge to 299 // do so. 300 if err := applyPreimage(preimage); err != nil { 301 return nil, err 302 } 303 304 return h.htlcSuccessResolver, nil 305 } 306 307 witnessUpdates = preimageSubscription.WitnessUpdates 308 } 309 310 for { 311 select { 312 case preimage := <-witnessUpdates: 313 // We received a new preimage, but we need to ignore 314 // all except the preimage we are waiting for. 315 if !preimage.Matches(h.htlc.RHash) { 316 continue 317 } 318 319 if err := applyPreimage(preimage); err != nil { 320 return nil, err 321 } 322 323 // We've learned of the preimage and this information 324 // has been added to our inner resolver. We return it so 325 // it can continue contract resolution. 326 return h.htlcSuccessResolver, nil 327 328 case hodlItem := <-hodlChan: 329 htlcResolution := hodlItem.(invoices.HtlcResolution) 330 return processHtlcResolution(htlcResolution) 331 332 case newBlock, ok := <-blockEpochs.Epochs: 333 if !ok { 334 return nil, errResolverShuttingDown 335 } 336 337 // If this new height expires the HTLC, then this means 338 // we never found out the preimage, so we can mark 339 // resolved and exit. 340 newHeight := uint32(newBlock.Height) 341 if newHeight >= h.htlcExpiry { 342 log.Infof("%T(%v): HTLC has timed out "+ 343 "(expiry=%v, height=%v), abandoning", h, 344 h.htlcResolution.ClaimOutpoint, 345 h.htlcExpiry, currentHeight) 346 h.resolved = true 347 348 report := h.report().resolverReport( 349 nil, 350 channeldb.ResolverTypeIncomingHtlc, 351 channeldb.ResolverOutcomeTimeout, 352 ) 353 return nil, h.Checkpoint(h, report) 354 } 355 356 case <-h.quit: 357 return nil, errResolverShuttingDown 358 } 359 } 360 } 361 362 // report returns a report on the resolution state of the contract. 363 func (h *htlcIncomingContestResolver) report() *ContractReport { 364 // No locking needed as these values are read-only. 365 366 finalAmt := h.htlc.Amt.ToAtoms() 367 if h.htlcResolution.SignedSuccessTx != nil { 368 finalAmt = dcrutil.Amount( 369 h.htlcResolution.SignedSuccessTx.TxOut[0].Value, 370 ) 371 } 372 373 return &ContractReport{ 374 Outpoint: h.htlcResolution.ClaimOutpoint, 375 Type: ReportOutputIncomingHtlc, 376 Amount: finalAmt, 377 MaturityHeight: h.htlcExpiry, 378 LimboBalance: finalAmt, 379 Stage: 1, 380 } 381 } 382 383 // Stop signals the resolver to cancel any current resolution processes, and 384 // suspend. 385 // 386 // NOTE: Part of the ContractResolver interface. 387 func (h *htlcIncomingContestResolver) Stop() { 388 close(h.quit) 389 } 390 391 // IsResolved returns true if the stored state in the resolve is fully 392 // resolved. In this case the target output can be forgotten. 393 // 394 // NOTE: Part of the ContractResolver interface. 395 func (h *htlcIncomingContestResolver) IsResolved() bool { 396 return h.resolved 397 } 398 399 // Encode writes an encoded version of the ContractResolver into the passed 400 // Writer. 401 // 402 // NOTE: Part of the ContractResolver interface. 403 func (h *htlcIncomingContestResolver) Encode(w io.Writer) error { 404 // We'll first write out the one field unique to this resolver. 405 if err := binary.Write(w, endian, h.htlcExpiry); err != nil { 406 return err 407 } 408 409 // Then we'll write out our internal resolver. 410 return h.htlcSuccessResolver.Encode(w) 411 } 412 413 // newIncomingContestResolverFromReader attempts to decode an encoded ContractResolver 414 // from the passed Reader instance, returning an active ContractResolver 415 // instance. 416 func newIncomingContestResolverFromReader(r io.Reader, resCfg ResolverConfig) ( 417 *htlcIncomingContestResolver, error) { 418 419 h := &htlcIncomingContestResolver{} 420 421 // We'll first read the one field unique to this resolver. 422 if err := binary.Read(r, endian, &h.htlcExpiry); err != nil { 423 return nil, err 424 } 425 426 // Then we'll decode our internal resolver. 427 successResolver, err := newSuccessResolverFromReader(r, resCfg) 428 if err != nil { 429 return nil, err 430 } 431 h.htlcSuccessResolver = successResolver 432 433 return h, nil 434 } 435 436 // Supplement adds additional information to the resolver that is required 437 // before Resolve() is called. 438 // 439 // NOTE: Part of the htlcContractResolver interface. 440 func (h *htlcIncomingContestResolver) Supplement(htlc channeldb.HTLC) { 441 h.htlc = htlc 442 } 443 444 // SupplementState allows the user of a ContractResolver to supplement it with 445 // state required for the proper resolution of a contract. 446 // 447 // NOTE: Part of the ContractResolver interface. 448 func (h *htlcIncomingContestResolver) SupplementState(_ *channeldb.OpenChannel) { 449 } 450 451 // decodePayload (re)decodes the hop payload of a received htlc. 452 func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload, error) { 453 454 onionReader := bytes.NewReader(h.htlc.OnionBlob) 455 iterator, err := h.OnionProcessor.ReconstructHopIterator( 456 onionReader, h.htlc.RHash[:], 457 ) 458 if err != nil { 459 return nil, err 460 } 461 462 return iterator.HopPayload() 463 } 464 465 // A compile time assertion to ensure htlcIncomingContestResolver meets the 466 // ContractResolver interface. 467 var _ htlcContractResolver = (*htlcIncomingContestResolver)(nil)