@@ -2001,23 +2001,29 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
20012001 handleRemoteSpentNext(tx, d1)
20022002 } else {
20032003 // Our counterparty is trying to broadcast a revoked commit tx (cheating attempt).
2004- // 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.
2005- val remoteCommit = d.commitments.latest.remoteCommit
2006- val nextRemoteCommit_opt = d.commitments.latest.nextRemoteCommit_opt.map(_.commit)
2007- val pendingOutgoingHtlcs = nextRemoteCommit_opt.getOrElse(remoteCommit).spec.htlcs.collect(DirectedHtlc .incoming)
2008- val failedHtlcs = Closing .recentlyFailedHtlcs(remoteCommit, nextRemoteCommit_opt, d.commitments.changes)
2009- (pendingOutgoingHtlcs ++ failedHtlcs).foreach { add =>
2004+ // We need to fail pending outgoing HTLCs, otherwise they will timeout upstream.
2005+ // We must do it here because since we're overwriting the commitments data, we will lose all information
2006+ // about HTLCs that are in the current commitments but were not in the revoked one.
2007+ // We fail *all* outgoing HTLCs:
2008+ // - those that are not in the revoked commitment will never settle on-chain
2009+ // - those that are in the revoked commitment will be claimed on-chain, so it's as if they were failed
2010+ // Note that if we already received the preimage for some of these HTLCs, we already relayed it upstream
2011+ // so the fail command will be a no-op.
2012+ val outgoingHtlcs = d.commitments.latest.localCommit.spec.htlcs.collect(DirectedHtlc .outgoing) ++
2013+ d.commitments.latest.remoteCommit.spec.htlcs.collect(DirectedHtlc .incoming) ++
2014+ d.commitments.latest.nextRemoteCommit_opt.map(_.commit.spec.htlcs.collect(DirectedHtlc .incoming)).getOrElse(Set .empty)
2015+ outgoingHtlcs.foreach { add =>
20102016 d.commitments.originChannels.get(add.id) match {
20112017 case Some (origin) =>
2012- log.info(s " failing htlc # ${add.id } paymentHash= ${add.paymentHash } origin= $origin : overridden by revoked remote commit " )
2018+ log.info(" failing htlc #{ } paymentHash={ } origin={} : overridden by revoked remote commit" , add.id, add.paymentHash, origin )
20132019 relayer ! RES_ADD_SETTLED (origin, add, HtlcResult .OnChainFail (HtlcOverriddenByLocalCommit (d.channelId, add)))
20142020 case None => ()
20152021 }
20162022 }
20172023 handleRemoteSpentOther(tx, d1)
20182024 }
20192025 case None =>
2020- log.warning(s " ignoring unrecognized alternative commit tx= ${ tx.txid} " )
2026+ log.warning(" ignoring unrecognized alternative commit tx={} " , tx.txid)
20212027 stay()
20222028 }
20232029
@@ -2028,20 +2034,22 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
20282034 // when a remote or local commitment tx containing outgoing htlcs is published on the network,
20292035 // we watch it in order to extract payment preimage if funds are pulled by the counterparty
20302036 // we can then use these preimages to fulfill origin htlcs
2031- log.debug(s " processing bitcoin output spent by txid= ${tx.txid } tx= $tx " )
2037+ log.debug(s " processing bitcoin output spent by txid={ } tx={} " , tx.txid, tx )
20322038 val extracted = Closing .extractPreimages(d.commitments.latest, tx)
20332039 extracted.foreach { case (htlc, preimage) =>
20342040 d.commitments.originChannels.get(htlc.id) match {
20352041 case Some (origin) =>
2036- log.info(s " fulfilling htlc # ${htlc.id } paymentHash= ${htlc.paymentHash } origin= $origin " )
2042+ log.info(" fulfilling htlc #{ } paymentHash={ } origin={} " , htlc.id, htlc.paymentHash, origin )
20372043 relayer ! RES_ADD_SETTLED (origin, htlc, HtlcResult .OnChainFulfill (preimage))
20382044 case None =>
20392045 // if we don't have the origin, it means that we already have forwarded the fulfill so that's not a big deal.
20402046 // this can happen if they send a signature containing the fulfill, then fail the channel before we have time to sign it
2041- log.info( s " cannot fulfill htlc # ${htlc.id } paymentHash= ${htlc.paymentHash } (origin not found) " )
2047+ log.warning( " cannot fulfill htlc #{ } paymentHash={ } (origin not found)" , htlc.id, htlc.paymentHash )
20422048 }
20432049 }
20442050 val revokedCommitPublished1 = d.revokedCommitPublished.map { rev =>
2051+ // this transaction may be an HTLC transaction spending a revoked commitment
2052+ // in that case, we immediately publish an HTLC-penalty transaction spending its output(s)
20452053 val (rev1, penaltyTxs) = Closing .RevokedClose .claimHtlcTxOutputs(keyManager, d.commitments.params, d.commitments.remotePerCommitmentSecrets, rev, tx, nodeParams.currentBitcoinCoreFeerates, d.finalScriptPubKey)
20462054 penaltyTxs.foreach(claimTx => txPublisher ! PublishFinalTx (claimTx, claimTx.fee, None ))
20472055 penaltyTxs.foreach(claimTx => blockchain ! WatchOutputSpent (self, tx.txid, claimTx.input.outPoint.index.toInt, claimTx.amountIn, hints = Set (claimTx.tx.txid)))
@@ -2050,7 +2058,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
20502058 stay() using d.copy(revokedCommitPublished = revokedCommitPublished1) storing()
20512059
20522060 case Event (WatchTxConfirmedTriggered (blockHeight, _, tx), d : DATA_CLOSING ) =>
2053- log.info(s " txid= ${tx.txid } has reached mindepth, updating closing state " )
2061+ log.info(" txid={ } has reached mindepth, updating closing state" , tx.txid )
20542062 context.system.eventStream.publish(TransactionConfirmed (d.channelId, remoteNodeId, tx))
20552063 // first we check if this tx belongs to one of the current local/remote commits, update it and update the channel data
20562064 val d1 = d.copy(
@@ -2101,27 +2109,31 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
21012109 val timedOutHtlcs = Closing .isClosingTypeAlreadyKnown(d1) match {
21022110 case Some (c : Closing .LocalClose ) => Closing .trimmedOrTimedOutHtlcs(d.commitments.params.commitmentFormat, c.localCommit, c.localCommitPublished, d.commitments.params.localParams.dustLimit, tx)
21032111 case Some (c : Closing .RemoteClose ) => Closing .trimmedOrTimedOutHtlcs(d.commitments.params.commitmentFormat, c.remoteCommit, c.remoteCommitPublished, d.commitments.params.remoteParams.dustLimit, tx)
2104- case _ => Set .empty[UpdateAddHtlc ] // we lose htlc outputs in dataloss protection scenarios (future remote commit)
2112+ case Some (_ : Closing .RevokedClose ) => Set .empty[UpdateAddHtlc ] // revoked commitments are handled using [[overriddenOutgoingHtlcs]] below
2113+ case Some (_ : Closing .RecoveryClose ) => Set .empty[UpdateAddHtlc ] // we lose htlc outputs in dataloss protection scenarios (future remote commit)
2114+ case Some (_ : Closing .MutualClose ) => Set .empty[UpdateAddHtlc ]
2115+ case None => Set .empty[UpdateAddHtlc ]
21052116 }
21062117 timedOutHtlcs.foreach { add =>
21072118 d.commitments.originChannels.get(add.id) match {
21082119 case Some (origin) =>
2109- log.info(s " failing htlc # ${add.id } paymentHash= ${add.paymentHash } origin= $origin : htlc timed out " )
2120+ log.info(" failing htlc #{ } paymentHash={ } origin={} : htlc timed out" , add.id, add.paymentHash, origin )
21102121 relayer ! RES_ADD_SETTLED (origin, add, HtlcResult .OnChainFail (HtlcsTimedoutDownstream (d.channelId, Set (add))))
21112122 case None =>
21122123 // same as for fulfilling the htlc (no big deal)
2113- log.info(s " cannot fail timed out htlc # ${add.id } paymentHash= ${add.paymentHash } (origin not found) " )
2124+ log.info(" cannot fail timed out htlc #{ } paymentHash={ } (origin not found)" , add.id, add.paymentHash )
21142125 }
21152126 }
21162127 // we also need to fail outgoing htlcs that we know will never reach the blockchain
2128+ // if we previously received the preimage, we have already relayed it upstream and the command below will be ignored
21172129 Closing .overriddenOutgoingHtlcs(d, tx).foreach { add =>
21182130 d.commitments.originChannels.get(add.id) match {
21192131 case Some (origin) =>
2120- log.info(s " failing htlc # ${add.id } paymentHash= ${add.paymentHash } origin= $origin : overridden by local commit " )
2132+ log.info(" failing htlc #{ } paymentHash={ } origin={} : overridden by local commit" , add.id, add.paymentHash, origin )
21212133 relayer ! RES_ADD_SETTLED (origin, add, HtlcResult .OnChainFail (HtlcOverriddenByLocalCommit (d.channelId, add)))
21222134 case None =>
21232135 // same as for fulfilling the htlc (no big deal)
2124- log.info(s " cannot fail overridden htlc # ${add.id } paymentHash= ${add.paymentHash } (origin not found) " )
2136+ log.info(" cannot fail overridden htlc #{ } paymentHash={ } (origin not found)" , add.id, add.paymentHash )
21252137 }
21262138 }
21272139 // for our outgoing payments, let's send events if we know that they will settle on chain
@@ -2295,8 +2307,8 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
22952307 case _ => Set .empty
22962308 }
22972309 val lastFundingLockedTlvs : Set [ChannelReestablishTlv ] = if (d.commitments.params.remoteParams.initFeatures.hasFeature(Features .SplicePrototype )) {
2298- d.commitments.lastLocalLocked_opt.map(c => ChannelReestablishTlv .MyCurrentFundingLockedTlv (c.fundingTxId)).toSet ++
2299- d.commitments.lastRemoteLocked_opt.map(c => ChannelReestablishTlv .YourLastFundingLockedTlv (c.fundingTxId)).toSet
2310+ d.commitments.lastLocalLocked_opt.map(c => ChannelReestablishTlv .MyCurrentFundingLockedTlv (c.fundingTxId)).toSet ++
2311+ d.commitments.lastRemoteLocked_opt.map(c => ChannelReestablishTlv .YourLastFundingLockedTlv (c.fundingTxId)).toSet
23002312 } else Set .empty
23012313
23022314 val channelReestablish = ChannelReestablish (
@@ -2996,11 +3008,12 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
29963008 /** Fail outgoing unsigned htlcs right away when transitioning from NORMAL to CLOSING */
29973009 onTransition {
29983010 case NORMAL -> CLOSING =>
2999- ( nextStateData : @ unchecked) match {
3011+ nextStateData match {
30003012 case d : DATA_CLOSING =>
30013013 d.commitments.changes.localChanges.proposed.collect {
30023014 case add : UpdateAddHtlc => relayer ! RES_ADD_SETTLED (d.commitments.originChannels(add.id), add, HtlcResult .ChannelFailureBeforeSigned )
30033015 }
3016+ case _ => ()
30043017 }
30053018 }
30063019
0 commit comments