github.com/decred/dcrlnd@v0.7.6/contractcourt/htlc_timeout_resolver.go (about) 1 package contractcourt 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "math" 8 "sync" 9 10 "github.com/davecgh/go-spew/spew" 11 "github.com/decred/dcrd/dcrutil/v4" 12 "github.com/decred/dcrd/wire" 13 "github.com/decred/dcrlnd/chainntnfs" 14 "github.com/decred/dcrlnd/channeldb" 15 "github.com/decred/dcrlnd/input" 16 "github.com/decred/dcrlnd/lntypes" 17 "github.com/decred/dcrlnd/lnwallet" 18 "github.com/decred/dcrlnd/lnwire" 19 "github.com/decred/dcrlnd/sweep" 20 ) 21 22 // htlcTimeoutResolver is a ContractResolver that's capable of resolving an 23 // outgoing HTLC. The HTLC may be on our commitment transaction, or on the 24 // commitment transaction of the remote party. An output on our commitment 25 // transaction is considered fully resolved once the second-level transaction 26 // has been confirmed (and reached a sufficient depth). An output on the 27 // commitment transaction of the remote party is resolved once we detect a 28 // spend of the direct HTLC output using the timeout clause. 29 type htlcTimeoutResolver struct { 30 // htlcResolution contains all the information required to properly 31 // resolve this outgoing HTLC. 32 htlcResolution lnwallet.OutgoingHtlcResolution 33 34 // outputIncubating returns true if we've sent the output to the output 35 // incubator (utxo nursery). 36 outputIncubating bool 37 38 // resolved reflects if the contract has been fully resolved or not. 39 resolved bool 40 41 // broadcastHeight is the height that the original contract was 42 // broadcast to the main-chain at. We'll use this value to bound any 43 // historical queries to the chain for spends/confirmations. 44 // 45 // TODO(roasbeef): wrap above into definite resolution embedding? 46 broadcastHeight uint32 47 48 // htlc contains information on the htlc that we are resolving 49 // on-chain. 50 htlc channeldb.HTLC 51 52 // channelInitiator denotes whether the party responsible for resolving 53 // the contract initiated the channel. 54 channelInitiator bool 55 56 // leaseExpiry denotes the additional waiting period the contract must 57 // hold until it can be resolved. This waiting period is known as the 58 // expiration of a script-enforced leased channel and only applies to 59 // the channel initiator. 60 // 61 // NOTE: This value should only be set when the contract belongs to a 62 // leased channel. 63 leaseExpiry uint32 64 65 // currentReport stores the current state of the resolver for reporting 66 // over the rpc interface. This should only be reported in case we have 67 // a non-nil SignDetails on the htlcResolution, otherwise the nursery 68 // will produce reports. 69 currentReport ContractReport 70 71 // reportLock prevents concurrent access to the resolver report. 72 reportLock sync.Mutex 73 74 contractResolverKit 75 } 76 77 // newTimeoutResolver instantiates a new timeout htlc resolver. 78 func newTimeoutResolver(res lnwallet.OutgoingHtlcResolution, 79 broadcastHeight uint32, htlc channeldb.HTLC, 80 resCfg ResolverConfig) *htlcTimeoutResolver { 81 82 h := &htlcTimeoutResolver{ 83 contractResolverKit: *newContractResolverKit(resCfg), 84 htlcResolution: res, 85 broadcastHeight: broadcastHeight, 86 htlc: htlc, 87 } 88 89 h.initReport() 90 91 return h 92 } 93 94 // ResolverKey returns an identifier which should be globally unique for this 95 // particular resolver within the chain the original contract resides within. 96 // 97 // NOTE: Part of the ContractResolver interface. 98 func (h *htlcTimeoutResolver) ResolverKey() []byte { 99 // The primary key for this resolver will be the outpoint of the HTLC 100 // on the commitment transaction itself. If this is our commitment, 101 // then the output can be found within the signed timeout tx, 102 // otherwise, it's just the ClaimOutpoint. 103 var op wire.OutPoint 104 if h.htlcResolution.SignedTimeoutTx != nil { 105 op = h.htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint 106 } else { 107 op = h.htlcResolution.ClaimOutpoint 108 } 109 110 key := newResolverID(op) 111 return key[:] 112 } 113 114 const ( 115 // expectedRemoteWitnessSuccessSize is the expected size of the witness 116 // on the remote commitment transaction for an outgoing HTLC that is 117 // swept on-chain by them with pre-image. 118 expectedRemoteWitnessSuccessSize = 4 119 120 // remotePreimageIndex index within the witness on the remote 121 // commitment transaction that will hold they pre-image if they go to 122 // sweep it on chain. 123 remotePreimageIndex = 2 124 125 // localPreimageIndex is the index within the witness on the local 126 // commitment transaction for an outgoing HTLC that will hold the 127 // pre-image if the remote party sweeps it. 128 localPreimageIndex = 1 129 ) 130 131 // claimCleanUp is a helper method that's called once the HTLC output is spent 132 // by the remote party. It'll extract the preimage, add it to the global cache, 133 // and finally send the appropriate clean up message. 134 func (h *htlcTimeoutResolver) claimCleanUp( 135 commitSpend *chainntnfs.SpendDetail) (ContractResolver, error) { 136 137 // Depending on if this is our commitment or not, then we'll be looking 138 // for a different witness pattern. 139 spenderIndex := commitSpend.SpenderInputIndex 140 spendingInput := commitSpend.SpendingTx.TxIn[spenderIndex] 141 142 log.Infof("%T(%v): extracting preimage! remote party spent "+ 143 "HTLC with tx=%v", h, h.htlcResolution.ClaimOutpoint, 144 spew.Sdump(commitSpend.SpendingTx)) 145 146 // Decode the sigScript of the spendingInput into a series of 147 // data pushes, so that we can extract the preimage. 148 // 149 // TODO(decred) verify whether we need to check the length of 150 // sigScriptPushes before trying to copy one of its elements. This 151 // depends on the particulars of how this call site is reached. 152 sigScriptPushes, err := input.SigScriptToWitnessStack(spendingInput.SignatureScript) 153 if err != nil { 154 return nil, err 155 156 } 157 158 // If this is the remote party's commitment, then we'll be looking for 159 // them to spend using the second-level success transaction. 160 var preimageBytes []byte 161 if h.htlcResolution.SignedTimeoutTx == nil { 162 // The witness stack when the remote party sweeps the output to 163 // them looks like: 164 // 165 // * <sender sig> <recvr sig> <preimage> <witness script> 166 preimageBytes = sigScriptPushes[2] 167 } else { 168 // Otherwise, they'll be spending directly from our commitment 169 // output. In which case the witness stack looks like: 170 // 171 // * <sig> <preimage> <witness script> 172 preimageBytes = sigScriptPushes[1] 173 } 174 175 preimage, err := lntypes.MakePreimage(preimageBytes) 176 if err != nil { 177 return nil, fmt.Errorf("unable to create pre-image from "+ 178 "witness: %v", err) 179 } 180 181 log.Infof("%T(%v): extracting preimage=%v from on-chain "+ 182 "spend!", h, h.htlcResolution.ClaimOutpoint, preimage) 183 184 // With the preimage obtained, we can now add it to the global cache. 185 if err := h.PreimageDB.AddPreimages(preimage); err != nil { 186 log.Errorf("%T(%v): unable to add witness to cache", 187 h, h.htlcResolution.ClaimOutpoint) 188 } 189 190 var pre [32]byte 191 copy(pre[:], preimage[:]) 192 193 // Finally, we'll send the clean up message, mark ourselves as 194 // resolved, then exit. 195 if err := h.DeliverResolutionMsg(ResolutionMsg{ 196 SourceChan: h.ShortChanID, 197 HtlcIndex: h.htlc.HtlcIndex, 198 PreImage: &pre, 199 }); err != nil { 200 return nil, err 201 } 202 h.resolved = true 203 204 // Checkpoint our resolver with a report which reflects the preimage 205 // claim by the remote party. 206 amt := dcrutil.Amount(h.htlcResolution.SweepSignDesc.Output.Value) 207 report := &channeldb.ResolverReport{ 208 OutPoint: h.htlcResolution.ClaimOutpoint, 209 Amount: amt, 210 ResolverType: channeldb.ResolverTypeOutgoingHtlc, 211 ResolverOutcome: channeldb.ResolverOutcomeClaimed, 212 SpendTxID: commitSpend.SpenderTxHash, 213 } 214 215 return nil, h.Checkpoint(h, report) 216 } 217 218 // chainDetailsToWatch returns the output and script which we use to watch for 219 // spends from the direct HTLC output on the commitment transaction. 220 // 221 // TODO(joostjager): output already set properly in 222 // lnwallet.newOutgoingHtlcResolution? And script too? 223 func (h *htlcTimeoutResolver) chainDetailsToWatch() (*wire.OutPoint, []byte, error) { 224 // If there's no timeout transaction, then the claim output is the 225 // output directly on the commitment transaction, so we'll just use 226 // that. 227 if h.htlcResolution.SignedTimeoutTx == nil { 228 outPointToWatch := h.htlcResolution.ClaimOutpoint 229 scriptToWatch := h.htlcResolution.SweepSignDesc.Output.PkScript 230 231 return &outPointToWatch, scriptToWatch, nil 232 } 233 234 // If this is the remote party's commitment, then we'll need to grab 235 // watch the output that our timeout transaction points to. We can 236 // directly grab the outpoint, then also extract the witness script 237 // (the last element of the witness stack) to re-construct the pkScript 238 // we need to watch. 239 outPointToWatch := h.htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint 240 sigScript := h.htlcResolution.SignedTimeoutTx.TxIn[0].SignatureScript 241 sigScriptPushes, err := input.SigScriptToWitnessStack(sigScript) 242 if err != nil { 243 return nil, nil, err 244 } 245 scriptToWatch, err := input.ScriptHashPkScript( 246 sigScriptPushes[len(sigScriptPushes)-1], 247 ) 248 if err != nil { 249 return nil, nil, err 250 } 251 252 return &outPointToWatch, scriptToWatch, nil 253 } 254 255 // isSuccessSpend returns true if the passed spend on the specified commitment 256 // is a success spend that reveals the pre-image or not. 257 func isSuccessSpend(spend *chainntnfs.SpendDetail, localCommit bool) bool { 258 // Based on the spending input index and transaction, obtain the 259 // witness that tells us what type of spend this is. 260 spenderIndex := spend.SpenderInputIndex 261 spendingInput := spend.SpendingTx.TxIn[spenderIndex] 262 spendingSigScript := spendingInput.SignatureScript 263 spendingWitness, err := input.SigScriptToWitnessStack(spendingSigScript) 264 if err != nil { 265 log.Errorf("Error decoding pushed data in sigScript: %v", err) 266 return false 267 } 268 269 // If this is the remote commitment then the only possible spends for 270 // outgoing HTLCs are: 271 // 272 // RECVR: <sender sig> <recvr sig> <preimage> (2nd level success spend) 273 // REVOK: <sig> <key> 274 // SENDR: <sig> 0 275 // 276 // In this case, if 5 witness elements are present (factoring the 277 // witness script), and the 3rd element is the size of the pre-image, 278 // then this is a remote spend. If not, then we swept it ourselves, or 279 // revoked their output. 280 if !localCommit { 281 return len(spendingWitness) == expectedRemoteWitnessSuccessSize && 282 len(spendingWitness[remotePreimageIndex]) == lntypes.HashSize 283 } 284 285 // Otherwise, for our commitment, the only possible spends for an 286 // outgoing HTLC are: 287 // 288 // SENDR: <sendr sig> <recvr sig> <0> (2nd level timeout) 289 // RECVR: <recvr sig> <preimage> 290 // REVOK: <revoke sig> <revoke key> 291 // 292 // So the only success case has the pre-image as the 2nd (index 1) 293 // element in the witness. 294 return len(spendingWitness[localPreimageIndex]) == lntypes.HashSize 295 } 296 297 // Resolve kicks off full resolution of an outgoing HTLC output. If it's our 298 // commitment, it isn't resolved until we see the second level HTLC txn 299 // confirmed. If it's the remote party's commitment, we don't resolve until we 300 // see a direct sweep via the timeout clause. 301 // 302 // NOTE: Part of the ContractResolver interface. 303 func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) { 304 // If we're already resolved, then we can exit early. 305 if h.resolved { 306 return nil, nil 307 } 308 309 // Start by spending the HTLC output, either by broadcasting the 310 // second-level timeout transaction, or directly if this is the remote 311 // commitment. 312 commitSpend, err := h.spendHtlcOutput() 313 if err != nil { 314 return nil, err 315 } 316 317 // If the spend reveals the pre-image, then we'll enter the clean up 318 // workflow to pass the pre-image back to the incoming link, add it to 319 // the witness cache, and exit. 320 if isSuccessSpend(commitSpend, h.htlcResolution.SignedTimeoutTx != nil) { 321 log.Infof("%T(%v): HTLC has been swept with pre-image by "+ 322 "remote party during timeout flow! Adding pre-image to "+ 323 "witness cache", h.htlcResolution.ClaimOutpoint) 324 325 return h.claimCleanUp(commitSpend) 326 } 327 328 log.Infof("%T(%v): resolving htlc with incoming fail msg, fully "+ 329 "confirmed", h, h.htlcResolution.ClaimOutpoint) 330 331 // At this point, the second-level transaction is sufficiently 332 // confirmed, or a transaction directly spending the output is. 333 // Therefore, we can now send back our clean up message, failing the 334 // HTLC on the incoming link. 335 failureMsg := &lnwire.FailPermanentChannelFailure{} 336 if err := h.DeliverResolutionMsg(ResolutionMsg{ 337 SourceChan: h.ShortChanID, 338 HtlcIndex: h.htlc.HtlcIndex, 339 Failure: failureMsg, 340 }); err != nil { 341 return nil, err 342 } 343 344 // Depending on whether this was a local or remote commit, we must 345 // handle the spending transaction accordingly. 346 return h.handleCommitSpend(commitSpend) 347 } 348 349 // spendHtlcOutput handles the initial spend of an HTLC output via the timeout 350 // clause. If this is our local commitment, the second-level timeout TX will be 351 // used to spend the output into the next stage. If this is the remote 352 // commitment, the output will be swept directly without the timeout 353 // transaction. 354 func (h *htlcTimeoutResolver) spendHtlcOutput() (*chainntnfs.SpendDetail, error) { 355 switch { 356 357 // If we have non-nil SignDetails, this means that have a 2nd level 358 // HTLC transaction that is signed using sighash SINGLE|ANYONECANPAY 359 // (the case for anchor type channels). In this case we can re-sign it 360 // and attach fees at will. We let the sweeper handle this job. 361 case h.htlcResolution.SignDetails != nil && !h.outputIncubating: 362 log.Infof("%T(%x): offering second-layer timeout tx to "+ 363 "sweeper: %v", h, h.htlc.RHash[:], 364 spew.Sdump(h.htlcResolution.SignedTimeoutTx)) 365 366 inp := input.MakeHtlcSecondLevelTimeoutAnchorInput( 367 h.htlcResolution.SignedTimeoutTx, 368 h.htlcResolution.SignDetails, 369 h.broadcastHeight, 370 ) 371 _, err := h.Sweeper.SweepInput( 372 &inp, 373 sweep.Params{ 374 Fee: sweep.FeePreference{ 375 ConfTarget: secondLevelConfTarget, 376 }, 377 }, 378 ) 379 if err != nil { 380 return nil, err 381 } 382 383 // If we have no SignDetails, and we haven't already sent the output to 384 // the utxo nursery, then we'll do so now. 385 case h.htlcResolution.SignDetails == nil && !h.outputIncubating: 386 log.Tracef("%T(%v): incubating htlc output", h, 387 h.htlcResolution.ClaimOutpoint) 388 389 err := h.IncubateOutputs( 390 h.ChanPoint, &h.htlcResolution, nil, 391 h.broadcastHeight, 392 ) 393 if err != nil { 394 return nil, err 395 } 396 397 h.outputIncubating = true 398 399 if err := h.Checkpoint(h); err != nil { 400 log.Errorf("unable to Checkpoint: %v", err) 401 return nil, err 402 } 403 } 404 405 // Now that we've handed off the HTLC to the nursery or sweeper, we'll 406 // watch for a spend of the output, and make our next move off of that. 407 // Depending on if this is our commitment, or the remote party's 408 // commitment, we'll be watching a different outpoint and script. 409 outpointToWatch, scriptToWatch, err := h.chainDetailsToWatch() 410 if err != nil { 411 return nil, err 412 } 413 414 log.Infof("%T(%v): waiting for HTLC output %v to be spent"+ 415 "fully confirmed", h, h.htlcResolution.ClaimOutpoint, 416 outpointToWatch) 417 418 // We'll block here until either we exit, or the HTLC output on the 419 // commitment transaction has been spent. 420 spend, err := waitForSpend( 421 outpointToWatch, scriptToWatch, h.broadcastHeight, 422 h.Notifier, h.quit, 423 ) 424 if err != nil { 425 return nil, err 426 } 427 428 // If this was the second level transaction published by the sweeper, 429 // we can checkpoint the resolver now that it's confirmed. 430 if h.htlcResolution.SignDetails != nil && !h.outputIncubating { 431 h.outputIncubating = true 432 if err := h.Checkpoint(h); err != nil { 433 log.Errorf("unable to Checkpoint: %v", err) 434 return nil, err 435 } 436 } 437 438 return spend, err 439 } 440 441 // handleCommitSpend handles the spend of the HTLC output on the commitment 442 // transaction. If this was our local commitment, the spend will be he 443 // confirmed second-level timeout transaction, and we'll sweep that into our 444 // wallet. If the was a remote commitment, the resolver will resolve 445 // immetiately. 446 func (h *htlcTimeoutResolver) handleCommitSpend( 447 commitSpend *chainntnfs.SpendDetail) (ContractResolver, error) { 448 449 var ( 450 // claimOutpoint will be the outpoint of the second level 451 // transaction, or on the remote commitment directly. It will 452 // start out as set in the resolution, but we'll update it if 453 // the second-level goes through the sweeper and changes its 454 // txid. 455 claimOutpoint = h.htlcResolution.ClaimOutpoint 456 457 // spendTxID will be the ultimate spend of the claimOutpoint. 458 // We set it to the commit spend for now, as this is the 459 // ultimate spend in case this is a remote commitment. If we go 460 // through the second-level transaction, we'll update this 461 // accordingly. 462 spendTxID = commitSpend.SpenderTxHash 463 464 reports []*channeldb.ResolverReport 465 ) 466 467 switch { 468 469 // If the sweeper is handling the second level transaction, wait for 470 // the CSV and possible CLTV lock to expire, before sweeping the output 471 // on the second-level. 472 case h.htlcResolution.SignDetails != nil: 473 waitHeight := uint32(commitSpend.SpendingHeight) + 474 h.htlcResolution.CsvDelay - 1 475 if h.hasCLTV() { 476 waitHeight = uint32(math.Max( 477 float64(waitHeight), float64(h.leaseExpiry), 478 )) 479 } 480 481 h.reportLock.Lock() 482 h.currentReport.Stage = 2 483 h.currentReport.MaturityHeight = waitHeight 484 h.reportLock.Unlock() 485 486 if h.hasCLTV() { 487 log.Infof("%T(%x): waiting for CSV and CLTV lock to "+ 488 "expire at height %v", h, h.htlc.RHash[:], 489 waitHeight) 490 } else { 491 log.Infof("%T(%x): waiting for CSV lock to expire at "+ 492 "height %v", h, h.htlc.RHash[:], waitHeight) 493 } 494 495 err := waitForHeight(waitHeight, h.Notifier, h.quit) 496 if err != nil { 497 return nil, err 498 } 499 500 // We'll use this input index to determine the second-level 501 // output index on the transaction, as the signatures requires 502 // the indexes to be the same. We don't look for the 503 // second-level output script directly, as there might be more 504 // than one HTLC output to the same pkScript. 505 op := &wire.OutPoint{ 506 Hash: *commitSpend.SpenderTxHash, 507 Index: commitSpend.SpenderInputIndex, 508 } 509 510 // Let the sweeper sweep the second-level output now that the 511 // CSV/CLTV locks have expired. 512 var inp *input.BaseInput 513 if h.hasCLTV() { 514 log.Infof("%T(%x): CSV and CLTV locks expired, offering "+ 515 "second-layer output to sweeper: %v", h, 516 h.htlc.RHash[:], op) 517 inp = input.NewCsvInputWithCltv( 518 op, input.LeaseHtlcOfferedTimeoutSecondLevel, 519 &h.htlcResolution.SweepSignDesc, 520 h.broadcastHeight, h.htlcResolution.CsvDelay, 521 h.leaseExpiry, 522 ) 523 } else { 524 log.Infof("%T(%x): CSV lock expired, offering "+ 525 "second-layer output to sweeper: %v", h, 526 h.htlc.RHash[:], op) 527 inp = input.NewCsvInput( 528 op, input.HtlcOfferedTimeoutSecondLevel, 529 &h.htlcResolution.SweepSignDesc, 530 h.broadcastHeight, h.htlcResolution.CsvDelay, 531 ) 532 } 533 _, err = h.Sweeper.SweepInput( 534 inp, 535 sweep.Params{ 536 Fee: sweep.FeePreference{ 537 ConfTarget: sweepConfTarget, 538 }, 539 }, 540 ) 541 if err != nil { 542 return nil, err 543 } 544 545 // Update the claim outpoint to point to the second-level 546 // transaction created by the sweeper. 547 claimOutpoint = *op 548 fallthrough 549 550 // Finally, if this was an output on our commitment transaction, we'll 551 // wait for the second-level HTLC output to be spent, and for that 552 // transaction itself to confirm. 553 case h.htlcResolution.SignedTimeoutTx != nil: 554 log.Infof("%T(%v): waiting for nursery/sweeper to spend CSV "+ 555 "delayed output", h, claimOutpoint) 556 sweep, err := waitForSpend( 557 &claimOutpoint, 558 h.htlcResolution.SweepSignDesc.Output.PkScript, 559 h.broadcastHeight, h.Notifier, h.quit, 560 ) 561 if err != nil { 562 return nil, err 563 } 564 565 // Update the spend txid to the hash of the sweep transaction. 566 spendTxID = sweep.SpenderTxHash 567 568 // Once our sweep of the timeout tx has confirmed, we add a 569 // resolution for our timeoutTx tx first stage transaction. 570 timeoutTx := commitSpend.SpendingTx 571 index := commitSpend.SpenderInputIndex 572 spendHash := commitSpend.SpenderTxHash 573 574 reports = append(reports, &channeldb.ResolverReport{ 575 OutPoint: timeoutTx.TxIn[index].PreviousOutPoint, 576 Amount: h.htlc.Amt.ToAtoms(), 577 ResolverType: channeldb.ResolverTypeOutgoingHtlc, 578 ResolverOutcome: channeldb.ResolverOutcomeFirstStage, 579 SpendTxID: spendHash, 580 }) 581 } 582 583 // With the clean up message sent, we'll now mark the contract 584 // resolved, update the recovered balance, record the timeout and the 585 // sweep txid on disk, and wait. 586 h.resolved = true 587 h.reportLock.Lock() 588 h.currentReport.RecoveredBalance = h.currentReport.LimboBalance 589 h.currentReport.LimboBalance = 0 590 h.reportLock.Unlock() 591 592 amt := dcrutil.Amount(h.htlcResolution.SweepSignDesc.Output.Value) 593 reports = append(reports, &channeldb.ResolverReport{ 594 OutPoint: claimOutpoint, 595 Amount: amt, 596 ResolverType: channeldb.ResolverTypeOutgoingHtlc, 597 ResolverOutcome: channeldb.ResolverOutcomeTimeout, 598 SpendTxID: spendTxID, 599 }) 600 601 return nil, h.Checkpoint(h, reports...) 602 } 603 604 // Stop signals the resolver to cancel any current resolution processes, and 605 // suspend. 606 // 607 // NOTE: Part of the ContractResolver interface. 608 func (h *htlcTimeoutResolver) Stop() { 609 close(h.quit) 610 } 611 612 // IsResolved returns true if the stored state in the resolve is fully 613 // resolved. In this case the target output can be forgotten. 614 // 615 // NOTE: Part of the ContractResolver interface. 616 func (h *htlcTimeoutResolver) IsResolved() bool { 617 return h.resolved 618 } 619 620 // report returns a report on the resolution state of the contract. 621 func (h *htlcTimeoutResolver) report() *ContractReport { 622 // If the sign details are nil, the report will be created by handled 623 // by the nursery. 624 if h.htlcResolution.SignDetails == nil { 625 return nil 626 } 627 628 h.reportLock.Lock() 629 defer h.reportLock.Unlock() 630 copy := h.currentReport 631 return © 632 } 633 634 func (h *htlcTimeoutResolver) initReport() { 635 // We create the initial report. This will only be reported for 636 // resolvers not handled by the nursery. 637 finalAmt := h.htlc.Amt.ToAtoms() 638 if h.htlcResolution.SignedTimeoutTx != nil { 639 finalAmt = dcrutil.Amount( 640 h.htlcResolution.SignedTimeoutTx.TxOut[0].Value, 641 ) 642 } 643 644 h.currentReport = ContractReport{ 645 Outpoint: h.htlcResolution.ClaimOutpoint, 646 Type: ReportOutputOutgoingHtlc, 647 Amount: finalAmt, 648 MaturityHeight: h.htlcResolution.Expiry, 649 LimboBalance: finalAmt, 650 Stage: 1, 651 } 652 } 653 654 // Encode writes an encoded version of the ContractResolver into the passed 655 // Writer. 656 // 657 // NOTE: Part of the ContractResolver interface. 658 func (h *htlcTimeoutResolver) Encode(w io.Writer) error { 659 // First, we'll write out the relevant fields of the 660 // OutgoingHtlcResolution to the writer. 661 if err := encodeOutgoingResolution(w, &h.htlcResolution); err != nil { 662 return err 663 } 664 665 // With that portion written, we can now write out the fields specific 666 // to the resolver itself. 667 if err := binary.Write(w, endian, h.outputIncubating); err != nil { 668 return err 669 } 670 if err := binary.Write(w, endian, h.resolved); err != nil { 671 return err 672 } 673 if err := binary.Write(w, endian, h.broadcastHeight); err != nil { 674 return err 675 } 676 677 if err := binary.Write(w, endian, h.htlc.HtlcIndex); err != nil { 678 return err 679 } 680 681 // We encode the sign details last for backwards compatibility. 682 err := encodeSignDetails(w, h.htlcResolution.SignDetails) 683 if err != nil { 684 return err 685 } 686 687 return nil 688 } 689 690 // newTimeoutResolverFromReader attempts to decode an encoded ContractResolver 691 // from the passed Reader instance, returning an active ContractResolver 692 // instance. 693 func newTimeoutResolverFromReader(r io.Reader, resCfg ResolverConfig) ( 694 *htlcTimeoutResolver, error) { 695 696 h := &htlcTimeoutResolver{ 697 contractResolverKit: *newContractResolverKit(resCfg), 698 } 699 700 // First, we'll read out all the mandatory fields of the 701 // OutgoingHtlcResolution that we store. 702 if err := decodeOutgoingResolution(r, &h.htlcResolution); err != nil { 703 return nil, err 704 } 705 706 // With those fields read, we can now read back the fields that are 707 // specific to the resolver itself. 708 if err := binary.Read(r, endian, &h.outputIncubating); err != nil { 709 return nil, err 710 } 711 if err := binary.Read(r, endian, &h.resolved); err != nil { 712 return nil, err 713 } 714 if err := binary.Read(r, endian, &h.broadcastHeight); err != nil { 715 return nil, err 716 } 717 718 if err := binary.Read(r, endian, &h.htlc.HtlcIndex); err != nil { 719 return nil, err 720 } 721 722 // Sign details is a new field that was added to the htlc resolution, 723 // so it is serialized last for backwards compatibility. We try to read 724 // it, but don't error out if there are not bytes left. 725 signDetails, err := decodeSignDetails(r) 726 if err == nil { 727 h.htlcResolution.SignDetails = signDetails 728 } else if err != io.EOF && err != io.ErrUnexpectedEOF { 729 return nil, err 730 } 731 732 h.initReport() 733 734 return h, nil 735 } 736 737 // Supplement adds additional information to the resolver that is required 738 // before Resolve() is called. 739 // 740 // NOTE: Part of the htlcContractResolver interface. 741 func (h *htlcTimeoutResolver) Supplement(htlc channeldb.HTLC) { 742 h.htlc = htlc 743 } 744 745 // SupplementState allows the user of a ContractResolver to supplement it with 746 // state required for the proper resolution of a contract. 747 // 748 // NOTE: Part of the ContractResolver interface. 749 func (h *htlcTimeoutResolver) SupplementState(state *channeldb.OpenChannel) { 750 if state.ChanType.HasLeaseExpiration() { 751 h.leaseExpiry = state.ThawHeight 752 } 753 h.channelInitiator = state.IsInitiator 754 } 755 756 // hasCLTV denotes whether the resolver must wait for an additional CLTV to 757 // expire before resolving the contract. 758 func (h *htlcTimeoutResolver) hasCLTV() bool { 759 return h.channelInitiator && h.leaseExpiry > 0 760 } 761 762 // HtlcPoint returns the htlc's outpoint on the commitment tx. 763 // 764 // NOTE: Part of the htlcContractResolver interface. 765 func (h *htlcTimeoutResolver) HtlcPoint() wire.OutPoint { 766 return h.htlcResolution.HtlcPoint() 767 } 768 769 // A compile time assertion to ensure htlcTimeoutResolver meets the 770 // ContractResolver interface. 771 var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)