mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-19 09:54:02 +01:00
Ignore pre-generated shutdown script when possible (#2738)
This is a follow up to #2565, more precisely to the _caveat_ here: https://github.com/ACINQ/eclair/pull/2565#issuecomment-1397052582. We now create a fresh shutdown script even if one was already generated at channel creation, if the channel doesn't have the mandatory `option_upfront_shutdown_script` negotiated. The (reasonable) assumption is that other implementations will ignore our pre-generated script if they didn't support the `option_upfront_shutdown_script` feature. This "on-the-fly" approach is simpler and safer than a db migration.
This commit is contained in:
parent
8d4205271d
commit
841a8d9b19
@ -125,7 +125,9 @@ case class ChannelParams(channelId: ByteVector32,
|
|||||||
// README: if we set our bitcoin node to generate taproot addresses and our peer does not support option_shutdown_anysegwit, we will not be able to mutual-close
|
// README: if we set our bitcoin node to generate taproot addresses and our peer does not support option_shutdown_anysegwit, we will not be able to mutual-close
|
||||||
// channels as the isValidFinalScriptPubkey() check would fail.
|
// channels as the isValidFinalScriptPubkey() check would fail.
|
||||||
val allowAnySegwit = Features.canUseFeature(localParams.initFeatures, remoteParams.initFeatures, Features.ShutdownAnySegwit)
|
val allowAnySegwit = Features.canUseFeature(localParams.initFeatures, remoteParams.initFeatures, Features.ShutdownAnySegwit)
|
||||||
if (localParams.upfrontShutdownScript_opt.exists(_ != localScriptPubKey)) Left(InvalidFinalScript(channelId))
|
val mustUseUpfrontShutdownScript = channelFeatures.hasFeature(Features.UpfrontShutdownScript)
|
||||||
|
// we only enforce using the pre-generated shutdown script if option_upfront_shutdown_script is set
|
||||||
|
if (mustUseUpfrontShutdownScript && localParams.upfrontShutdownScript_opt.exists(_ != localScriptPubKey)) Left(InvalidFinalScript(channelId))
|
||||||
else if (!Closing.MutualClose.isValidFinalScriptPubkey(localScriptPubKey, allowAnySegwit)) Left(InvalidFinalScript(channelId))
|
else if (!Closing.MutualClose.isValidFinalScriptPubkey(localScriptPubKey, allowAnySegwit)) Left(InvalidFinalScript(channelId))
|
||||||
else Right(localScriptPubKey)
|
else Right(localScriptPubKey)
|
||||||
}
|
}
|
||||||
@ -137,7 +139,9 @@ case class ChannelParams(channelId: ByteVector32,
|
|||||||
def validateRemoteShutdownScript(remoteScriptPubKey: ByteVector): Either[ChannelException, ByteVector] = {
|
def validateRemoteShutdownScript(remoteScriptPubKey: ByteVector): Either[ChannelException, ByteVector] = {
|
||||||
// to check whether shutdown_any_segwit is active we check features in local and remote parameters, which are negotiated each time we connect to our peer.
|
// to check whether shutdown_any_segwit is active we check features in local and remote parameters, which are negotiated each time we connect to our peer.
|
||||||
val allowAnySegwit = Features.canUseFeature(localParams.initFeatures, remoteParams.initFeatures, Features.ShutdownAnySegwit)
|
val allowAnySegwit = Features.canUseFeature(localParams.initFeatures, remoteParams.initFeatures, Features.ShutdownAnySegwit)
|
||||||
if (localParams.upfrontShutdownScript_opt.isDefined && remoteParams.upfrontShutdownScript_opt.exists(_ != remoteScriptPubKey)) Left(InvalidFinalScript(channelId))
|
val mustUseUpfrontShutdownScript = channelFeatures.hasFeature(Features.UpfrontShutdownScript)
|
||||||
|
// we only enforce using the pre-generated shutdown script if option_upfront_shutdown_script is set
|
||||||
|
if (mustUseUpfrontShutdownScript && remoteParams.upfrontShutdownScript_opt.exists(_ != remoteScriptPubKey)) Left(InvalidFinalScript(channelId))
|
||||||
else if (!Closing.MutualClose.isValidFinalScriptPubkey(remoteScriptPubKey, allowAnySegwit)) Left(InvalidFinalScript(channelId))
|
else if (!Closing.MutualClose.isValidFinalScriptPubkey(remoteScriptPubKey, allowAnySegwit)) Left(InvalidFinalScript(channelId))
|
||||||
else Right(remoteScriptPubKey)
|
else Right(remoteScriptPubKey)
|
||||||
}
|
}
|
||||||
|
@ -634,7 +634,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
}
|
}
|
||||||
if (d.remoteShutdown.isDefined && !commitments1.changes.localHasUnsignedOutgoingHtlcs) {
|
if (d.remoteShutdown.isDefined && !commitments1.changes.localHasUnsignedOutgoingHtlcs) {
|
||||||
// we were waiting for our pending htlcs to be signed before replying with our local shutdown
|
// we were waiting for our pending htlcs to be signed before replying with our local shutdown
|
||||||
val finalScriptPubKey = commitments1.params.localParams.upfrontShutdownScript_opt.getOrElse(getOrGenerateFinalScriptPubKey(d))
|
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
||||||
val localShutdown = Shutdown(d.channelId, finalScriptPubKey)
|
val localShutdown = Shutdown(d.channelId, finalScriptPubKey)
|
||||||
// note: it means that we had pending htlcs to sign, therefore we go to SHUTDOWN, not to NEGOTIATING
|
// note: it means that we had pending htlcs to sign, therefore we go to SHUTDOWN, not to NEGOTIATING
|
||||||
require(commitments1.latest.remoteCommit.spec.htlcs.nonEmpty, "we must have just signed new htlcs, otherwise we would have sent our Shutdown earlier")
|
require(commitments1.latest.remoteCommit.spec.htlcs.nonEmpty, "we must have just signed new htlcs, otherwise we would have sent our Shutdown earlier")
|
||||||
@ -656,7 +656,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
} else if (d.commitments.changes.localHasUnsignedOutgoingUpdateFee) {
|
} else if (d.commitments.changes.localHasUnsignedOutgoingUpdateFee) {
|
||||||
handleCommandError(CannotCloseWithUnsignedOutgoingUpdateFee(d.channelId), c)
|
handleCommandError(CannotCloseWithUnsignedOutgoingUpdateFee(d.channelId), c)
|
||||||
} else {
|
} else {
|
||||||
val localScriptPubKey = c.scriptPubKey.getOrElse(d.commitments.params.localParams.upfrontShutdownScript_opt.getOrElse(getOrGenerateFinalScriptPubKey(d)))
|
val localScriptPubKey = c.scriptPubKey.getOrElse(getOrGenerateFinalScriptPubKey(d))
|
||||||
d.commitments.params.validateLocalShutdownScript(localScriptPubKey) match {
|
d.commitments.params.validateLocalShutdownScript(localScriptPubKey) match {
|
||||||
case Left(e) => handleCommandError(e, c)
|
case Left(e) => handleCommandError(e, c)
|
||||||
case Right(localShutdownScript) =>
|
case Right(localShutdownScript) =>
|
||||||
|
@ -18,6 +18,7 @@ package fr.acinq.eclair.channel.fsm
|
|||||||
|
|
||||||
import akka.actor.{ActorRef, FSM, Status}
|
import akka.actor.{ActorRef, FSM, Status}
|
||||||
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Script}
|
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Script}
|
||||||
|
import fr.acinq.eclair.Features
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.db.PendingCommandsDb
|
import fr.acinq.eclair.db.PendingCommandsDb
|
||||||
import fr.acinq.eclair.io.Peer
|
import fr.acinq.eclair.io.Peer
|
||||||
@ -106,7 +107,20 @@ trait CommonHandlers {
|
|||||||
case d: DATA_SHUTDOWN => d.localShutdown.scriptPubKey
|
case d: DATA_SHUTDOWN => d.localShutdown.scriptPubKey
|
||||||
case d: DATA_NEGOTIATING => d.localShutdown.scriptPubKey
|
case d: DATA_NEGOTIATING => d.localShutdown.scriptPubKey
|
||||||
case d: DATA_CLOSING => d.finalScriptPubKey
|
case d: DATA_CLOSING => d.finalScriptPubKey
|
||||||
case d => d.commitments.params.localParams.upfrontShutdownScript_opt.getOrElse(generateFinalScriptPubKey())
|
case d =>
|
||||||
|
d.commitments.params.localParams.upfrontShutdownScript_opt match {
|
||||||
|
case Some(upfrontShutdownScript) =>
|
||||||
|
if (data.commitments.params.channelFeatures.hasFeature(Features.UpfrontShutdownScript)) {
|
||||||
|
// we have a shutdown script, and the option_upfront_shutdown_script is enabled: we have to use it
|
||||||
|
upfrontShutdownScript
|
||||||
|
} else {
|
||||||
|
log.info("ignoring pre-generated shutdown script, because option_upfront_shutdown_script is disabled")
|
||||||
|
generateFinalScriptPubKey()
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
// normal case: we don't pre-generate shutdown scripts
|
||||||
|
generateFinalScriptPubKey()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def generateFinalScriptPubKey(): ByteVector = {
|
private def generateFinalScriptPubKey(): ByteVector = {
|
||||||
|
Loading…
Reference in New Issue
Block a user