@@ -1994,23 +1994,29 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
19941994 handleRemoteSpentNext(tx, d1)
19951995 } else {
19961996 // Our counterparty is trying to broadcast a revoked commit tx (cheating attempt).
1997- // We need to fail pending outgoing HTLCs: we must do it here because we're overwriting the commitments data, so we won't be able to do it afterwards.
1998- val remoteCommit = d.commitments.latest.remoteCommit
1999- val nextRemoteCommit_opt = d.commitments.latest.nextRemoteCommit_opt.map(_.commit)
2000- val pendingOutgoingHtlcs = nextRemoteCommit_opt.getOrElse(remoteCommit).spec.htlcs.collect(DirectedHtlc .incoming)
2001- val failedHtlcs = Closing .recentlyFailedHtlcs(remoteCommit, nextRemoteCommit_opt, d.commitments.changes)
2002- (pendingOutgoingHtlcs ++ failedHtlcs).foreach { add =>
1997+ // We need to fail pending outgoing HTLCs, otherwise they will timeout upstream.
1998+ // We must do it here because since we're overwriting the commitments data, we will lose all information
1999+ // about HTLCs that are in the current commitments but were not in the revoked one.
2000+ // We fail *all* outgoing HTLCs:
2001+ // - those that are not in the revoked commitment will never settle on-chain
2002+ // - those that are in the revoked commitment will be claimed on-chain, so it's as if they were failed
2003+ // Note that if we already received the preimage for some of these HTLCs, we already relayed it upstream
2004+ // so the fail command will be a no-op.
2005+ val outgoingHtlcs = d.commitments.latest.localCommit.spec.htlcs.collect(DirectedHtlc .outgoing) ++
2006+ d.commitments.latest.remoteCommit.spec.htlcs.collect(DirectedHtlc .incoming) ++
2007+ d.commitments.latest.nextRemoteCommit_opt.map(_.commit.spec.htlcs.collect(DirectedHtlc .incoming)).getOrElse(Set .empty)
2008+ outgoingHtlcs.foreach { add =>
20032009 d.commitments.originChannels.get(add.id) match {
20042010 case Some (origin) =>
2005- log.info(s " failing htlc # ${add.id } paymentHash= ${add.paymentHash } origin= $origin : overridden by revoked remote commit " )
2011+ log.info(" failing htlc #{ } paymentHash={ } origin={} : overridden by revoked remote commit" , add.id, add.paymentHash, origin )
20062012 relayer ! RES_ADD_SETTLED (origin, add, HtlcResult .OnChainFail (HtlcOverriddenByLocalCommit (d.channelId, add)))
20072013 case None => ()
20082014 }
20092015 }
20102016 handleRemoteSpentOther(tx, d1)
20112017 }
20122018 case None =>
2013- log.warning(s " ignoring unrecognized alternative commit tx= ${ tx.txid} " )
2019+ log.warning(" ignoring unrecognized alternative commit tx={} " , tx.txid)
20142020 stay()
20152021 }
20162022
@@ -2021,20 +2027,22 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
20212027 // when a remote or local commitment tx containing outgoing htlcs is published on the network,
20222028 // we watch it in order to extract payment preimage if funds are pulled by the counterparty
20232029 // we can then use these preimages to fulfill origin htlcs
2024- log.debug(s " processing bitcoin output spent by txid= ${tx.txid } tx= $tx " )
2030+ log.debug(s " processing bitcoin output spent by txid={ } tx={} " , tx.txid, tx )
20252031 val extracted = Closing .extractPreimages(d.commitments.latest, tx)
20262032 extracted.foreach { case (htlc, preimage) =>
20272033 d.commitments.originChannels.get(htlc.id) match {
20282034 case Some (origin) =>
2029- log.info(s " fulfilling htlc # ${htlc.id } paymentHash= ${htlc.paymentHash } origin= $origin " )
2035+ log.info(" fulfilling htlc #{ } paymentHash={ } origin={} " , htlc.id, htlc.paymentHash, origin )
20302036 relayer ! RES_ADD_SETTLED (origin, htlc, HtlcResult .OnChainFulfill (preimage))
20312037 case None =>
20322038 // if we don't have the origin, it means that we already have forwarded the fulfill so that's not a big deal.
20332039 // this can happen if they send a signature containing the fulfill, then fail the channel before we have time to sign it
2034- log.info( s " cannot fulfill htlc # ${htlc.id } paymentHash= ${htlc.paymentHash } (origin not found) " )
2040+ log.warning( " cannot fulfill htlc #{ } paymentHash={ } (origin not found)" , htlc.id, htlc.paymentHash )
20352041 }
20362042 }
20372043 val revokedCommitPublished1 = d.revokedCommitPublished.map { rev =>
2044+ // this transaction may be an HTLC transaction spending a revoked commitment
2045+ // in that case, we immediately publish an HTLC-penalty transaction spending its output(s)
20382046 val (rev1, penaltyTxs) = Closing .RevokedClose .claimHtlcTxOutputs(keyManager, d.commitments.params, d.commitments.remotePerCommitmentSecrets, rev, tx, nodeParams.currentBitcoinCoreFeerates, d.finalScriptPubKey)
20392047 penaltyTxs.foreach(claimTx => txPublisher ! PublishFinalTx (claimTx, claimTx.fee, None ))
20402048 penaltyTxs.foreach(claimTx => blockchain ! WatchOutputSpent (self, tx.txid, claimTx.input.outPoint.index.toInt, claimTx.amountIn, hints = Set (claimTx.tx.txid)))
@@ -2043,7 +2051,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
20432051 stay() using d.copy(revokedCommitPublished = revokedCommitPublished1) storing()
20442052
20452053 case Event (WatchTxConfirmedTriggered (blockHeight, _, tx), d : DATA_CLOSING ) =>
2046- log.info(s " txid= ${tx.txid } has reached mindepth, updating closing state " )
2054+ log.info(" txid={ } has reached mindepth, updating closing state" , tx.txid )
20472055 context.system.eventStream.publish(TransactionConfirmed (d.channelId, remoteNodeId, tx))
20482056 // first we check if this tx belongs to one of the current local/remote commits, update it and update the channel data
20492057 val d1 = d.copy(
@@ -2094,27 +2102,31 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
20942102 val timedOutHtlcs = Closing .isClosingTypeAlreadyKnown(d1) match {
20952103 case Some (c : Closing .LocalClose ) => Closing .trimmedOrTimedOutHtlcs(d.commitments.params.commitmentFormat, c.localCommit, c.localCommitPublished, d.commitments.params.localParams.dustLimit, tx)
20962104 case Some (c : Closing .RemoteClose ) => Closing .trimmedOrTimedOutHtlcs(d.commitments.params.commitmentFormat, c.remoteCommit, c.remoteCommitPublished, d.commitments.params.remoteParams.dustLimit, tx)
2097- case _ => Set .empty[UpdateAddHtlc ] // we lose htlc outputs in dataloss protection scenarios (future remote commit)
2105+ case Some (_ : Closing .RevokedClose ) => Set .empty[UpdateAddHtlc ] // revoked commitments are handled using [[overriddenOutgoingHtlcs]] below
2106+ case Some (_ : Closing .RecoveryClose ) => Set .empty[UpdateAddHtlc ] // we lose htlc outputs in dataloss protection scenarios (future remote commit)
2107+ case Some (_ : Closing .MutualClose ) => Set .empty[UpdateAddHtlc ]
2108+ case None => Set .empty[UpdateAddHtlc ]
20982109 }
20992110 timedOutHtlcs.foreach { add =>
21002111 d.commitments.originChannels.get(add.id) match {
21012112 case Some (origin) =>
2102- log.info(s " failing htlc # ${add.id } paymentHash= ${add.paymentHash } origin= $origin : htlc timed out " )
2113+ log.info(" failing htlc #{ } paymentHash={ } origin={} : htlc timed out" , add.id, add.paymentHash, origin )
21032114 relayer ! RES_ADD_SETTLED (origin, add, HtlcResult .OnChainFail (HtlcsTimedoutDownstream (d.channelId, Set (add))))
21042115 case None =>
21052116 // same as for fulfilling the htlc (no big deal)
2106- log.info(s " cannot fail timed out htlc # ${add.id } paymentHash= ${add.paymentHash } (origin not found) " )
2117+ log.info(" cannot fail timed out htlc #{ } paymentHash={ } (origin not found)" , add.id, add.paymentHash )
21072118 }
21082119 }
21092120 // we also need to fail outgoing htlcs that we know will never reach the blockchain
2121+ // if we previously received the preimage, we have already relayed it upstream and the command below will be ignored
21102122 Closing .overriddenOutgoingHtlcs(d, tx).foreach { add =>
21112123 d.commitments.originChannels.get(add.id) match {
21122124 case Some (origin) =>
2113- log.info(s " failing htlc # ${add.id } paymentHash= ${add.paymentHash } origin= $origin : overridden by local commit " )
2125+ log.info(" failing htlc #{ } paymentHash={ } origin={} : overridden by local commit" , add.id, add.paymentHash, origin )
21142126 relayer ! RES_ADD_SETTLED (origin, add, HtlcResult .OnChainFail (HtlcOverriddenByLocalCommit (d.channelId, add)))
21152127 case None =>
21162128 // same as for fulfilling the htlc (no big deal)
2117- log.info(s " cannot fail overridden htlc # ${add.id } paymentHash= ${add.paymentHash } (origin not found) " )
2129+ log.info(" cannot fail overridden htlc #{ } paymentHash={ } (origin not found)" , add.id, add.paymentHash )
21182130 }
21192131 }
21202132 // for our outgoing payments, let's send events if we know that they will settle on chain
@@ -2288,8 +2300,8 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
22882300 case _ => Set .empty
22892301 }
22902302 val lastFundingLockedTlvs : Set [ChannelReestablishTlv ] = if (d.commitments.params.remoteParams.initFeatures.hasFeature(Features .SplicePrototype )) {
2291- d.commitments.lastLocalLocked_opt.map(c => ChannelReestablishTlv .MyCurrentFundingLockedTlv (c.fundingTxId)).toSet ++
2292- d.commitments.lastRemoteLocked_opt.map(c => ChannelReestablishTlv .YourLastFundingLockedTlv (c.fundingTxId)).toSet
2303+ d.commitments.lastLocalLocked_opt.map(c => ChannelReestablishTlv .MyCurrentFundingLockedTlv (c.fundingTxId)).toSet ++
2304+ d.commitments.lastRemoteLocked_opt.map(c => ChannelReestablishTlv .YourLastFundingLockedTlv (c.fundingTxId)).toSet
22932305 } else Set .empty
22942306
22952307 val channelReestablish = ChannelReestablish (
@@ -2989,11 +3001,12 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
29893001 /** Fail outgoing unsigned htlcs right away when transitioning from NORMAL to CLOSING */
29903002 onTransition {
29913003 case NORMAL -> CLOSING =>
2992- ( nextStateData : @ unchecked) match {
3004+ nextStateData match {
29933005 case d : DATA_CLOSING =>
29943006 d.commitments.changes.localChanges.proposed.collect {
29953007 case add : UpdateAddHtlc => relayer ! RES_ADD_SETTLED (d.commitments.originChannels(add.id), add, HtlcResult .ChannelFailureBeforeSigned )
29963008 }
3009+ case _ => ()
29973010 }
29983011 }
29993012
0 commit comments