diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/SpendFromChannelAddress.scala b/eclair-core/src/main/scala/fr/acinq/eclair/SpendFromChannelAddress.scala index 8f799def24..6281d2577d 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/SpendFromChannelAddress.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/SpendFromChannelAddress.scala @@ -4,8 +4,8 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey import fr.acinq.bitcoin.scalacompat.{ByteVector64, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxIn, TxOut, addressToPublicKeyScript} import fr.acinq.eclair.blockchain.fee.FeeratePerKw import fr.acinq.eclair.transactions.Scripts.multiSig2of2 +import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.transactions.{Scripts, Transactions} -import fr.acinq.eclair.transactions.Transactions.{DefaultCommitmentFormat, InputInfo, PlaceHolderPubKey, PlaceHolderSig, TxOwner} import scodec.bits.ByteVector import scala.concurrent.Future @@ -29,7 +29,7 @@ trait SpendFromChannelAddress { Right(pubKeyScript) = addressToPublicKeyScript(appKit.nodeParams.chainHash, address).map(Script.write) // build the tx a first time with a zero amount to compute the weight fee = Transactions.weight2fee(feerate, buildTx(outPoint, 0.sat, pubKeyScript, dummy2of2Witness).weight()) - _ = assert(inputAmount - fee > Transactions.dustLimit(pubKeyScript), s"amount insufficient (fee=$fee)") + _ = assert(inputAmount - fee > Scripts.dustLimit(pubKeyScript), s"amount insufficient (fee=$fee)") unsignedTx = buildTx(outPoint, inputAmount - fee, pubKeyScript, dummy2of2Witness) // the following are not used, but need to be sent to the counterparty localFundingPubkey = appKit.nodeParams.channelKeyManager.fundingPublicKey(fundingKeyPath, fundingTxIndex).publicKey diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala index 24a1448459..4065e38c7e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala @@ -558,6 +558,7 @@ object Helpers { } } } + } object Closing { @@ -816,7 +817,7 @@ object Helpers { * The various dust limits are detailed in https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#dust-limits */ def checkClosingDustAmounts(closingTx: ClosingTx): Boolean = { - closingTx.tx.txOut.forall(txOut => txOut.amount >= Transactions.dustLimit(txOut.publicKeyScript)) + closingTx.tx.txOut.forall(txOut => txOut.amount >= Scripts.dustLimit(txOut.publicKeyScript)) } } @@ -850,7 +851,7 @@ object Helpers { */ private def shouldUpdateAnchorTxs(anchorTxs: List[ClaimAnchorOutputTx], confirmationTarget: ConfirmationTarget): Boolean = { anchorTxs - .collect { case tx: ClaimLocalAnchorOutputTx => tx.confirmationTarget } + .collect { case tx: ClaimAnchorOutputTx => tx.confirmationTarget } .forall { case ConfirmationTarget.Absolute(current) => confirmationTarget match { case ConfirmationTarget.Absolute(proposed) => proposed < current @@ -912,11 +913,8 @@ object Helpers { val localFundingPubKey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey val claimAnchorTxs = List( withTxGenerationLog("local-anchor") { - Transactions.makeClaimLocalAnchorOutputTx(lcp.commitTx, localFundingPubKey, confirmationTarget) + Transactions.makeClaimAnchorOutputTx(lcp.commitTx, localFundingPubKey, confirmationTarget) }, - withTxGenerationLog("remote-anchor") { - Transactions.makeClaimRemoteAnchorOutputTx(lcp.commitTx, commitment.remoteFundingPubKey) - } ).flatten lcp.copy(claimAnchorTxs = claimAnchorTxs) } else { @@ -1001,6 +999,7 @@ object Helpers { (localCommitPublished, None) } } + } object RemoteClose { @@ -1040,12 +1039,9 @@ object Helpers { if (shouldUpdateAnchorTxs(rcp.claimAnchorTxs, confirmationTarget)) { val localFundingPubkey = keyManager.fundingPublicKey(commitment.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey val claimAnchorTxs = List( - withTxGenerationLog("local-anchor") { - Transactions.makeClaimLocalAnchorOutputTx(rcp.commitTx, localFundingPubkey, confirmationTarget) - }, withTxGenerationLog("remote-anchor") { - Transactions.makeClaimRemoteAnchorOutputTx(rcp.commitTx, commitment.remoteFundingPubKey) - } + Transactions.makeClaimAnchorOutputTx(rcp.commitTx, localFundingPubkey, confirmationTarget) + }, ).flatten rcp.copy(claimAnchorTxs = claimAnchorTxs) } else { @@ -1158,6 +1154,7 @@ object Helpers { }) }.toSeq.flatten.flatten.toMap } + } object RevokedClose { @@ -1328,6 +1325,7 @@ object Helpers { (revokedCommitPublished, Nil) } } + } /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala index 3e2e1bd78e..d492cf561d 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala @@ -2169,15 +2169,15 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with val nrcp1 = d.nextRemoteCommitPublished.map(nrcp => Closing.RemoteClose.claimAnchors(keyManager, d.commitments.latest, nrcp, c.confirmationTarget)) // We favor the remote commitment(s) because they're more interesting than the local commitment (no CSV delays). if (rcp1.nonEmpty) { - rcp1.foreach(rcp => rcp.claimAnchorTxs.collect { case tx: Transactions.ClaimLocalAnchorOutputTx => txPublisher ! PublishReplaceableTx(tx, d.commitments.latest, rcp.commitTx) }) + rcp1.foreach(rcp => rcp.claimAnchorTxs.foreach { tx => txPublisher ! PublishReplaceableTx(tx, d.commitments.latest, rcp.commitTx) }) c.replyTo ! RES_SUCCESS(c, d.channelId) stay() using d.copy(remoteCommitPublished = rcp1) storing() } else if (nrcp1.nonEmpty) { - nrcp1.foreach(rcp => rcp.claimAnchorTxs.collect { case tx: Transactions.ClaimLocalAnchorOutputTx => txPublisher ! PublishReplaceableTx(tx, d.commitments.latest, rcp.commitTx) }) + nrcp1.foreach(rcp => rcp.claimAnchorTxs.foreach { tx => txPublisher ! PublishReplaceableTx(tx, d.commitments.latest, rcp.commitTx) }) c.replyTo ! RES_SUCCESS(c, d.channelId) stay() using d.copy(nextRemoteCommitPublished = nrcp1) storing() } else if (lcp1.nonEmpty) { - lcp1.foreach(lcp => lcp.claimAnchorTxs.collect { case tx: Transactions.ClaimLocalAnchorOutputTx => txPublisher ! PublishReplaceableTx(tx, d.commitments.latest, lcp.commitTx) }) + lcp1.foreach(lcp => lcp.claimAnchorTxs.foreach { tx => txPublisher ! PublishReplaceableTx(tx, d.commitments.latest, lcp.commitTx) }) c.replyTo ! RES_SUCCESS(c, d.channelId) stay() using d.copy(localCommitPublished = lcp1) storing() } else { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/ErrorHandlers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/ErrorHandlers.scala index 361a846880..2b05fdb07f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/ErrorHandlers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/ErrorHandlers.scala @@ -235,7 +235,7 @@ trait ErrorHandlers extends CommonHandlers { List(PublishFinalTx(commitTx, commitment.commitInput.outPoint, commitment.capacity, "commit-tx", Closing.commitTxFee(commitment.commitInput, commitTx, localPaysCommitTxFees), None)) ++ (claimMainDelayedOutputTx.map(tx => PublishFinalTx(tx, tx.fee, None)) ++ redeemableHtlcTxs ++ claimHtlcDelayedTxs.map(tx => PublishFinalTx(tx, tx.fee, None))) case _: Transactions.AnchorOutputsCommitmentFormat => val redeemableHtlcTxs = htlcTxs.values.flatten.map(tx => PublishReplaceableTx(tx, commitment, commitTx)) - val claimLocalAnchor = claimAnchorTxs.collect { case tx: Transactions.ClaimLocalAnchorOutputTx if !localCommitPublished.isConfirmed => PublishReplaceableTx(tx, commitment, commitTx) } + val claimLocalAnchor = claimAnchorTxs.collect { case tx: Transactions.ClaimAnchorOutputTx if !localCommitPublished.isConfirmed => PublishReplaceableTx(tx, commitment, commitTx) } List(PublishFinalTx(commitTx, commitment.commitInput.outPoint, commitment.capacity, "commit-tx", Closing.commitTxFee(commitment.commitInput, commitTx, localPaysCommitTxFees), None)) ++ claimLocalAnchor ++ claimMainDelayedOutputTx.map(tx => PublishFinalTx(tx, tx.fee, None)) ++ redeemableHtlcTxs ++ claimHtlcDelayedTxs.map(tx => PublishFinalTx(tx, tx.fee, None)) } publishIfNeeded(publishQueue, irrevocablySpent) @@ -251,7 +251,7 @@ trait ErrorHandlers extends CommonHandlers { // We watch outputs of the commitment tx that both parties may spend. // We also watch our local anchor: this ensures that we will correctly detect when it's confirmed and count its fees // in the audit DB, even if we restart before confirmation. - val watchSpentQueue = htlcTxs.keys ++ claimAnchorTxs.collect { case tx: Transactions.ClaimLocalAnchorOutputTx if !localCommitPublished.isConfirmed => tx.input.outPoint } + val watchSpentQueue = htlcTxs.keys ++ claimAnchorTxs.collect { case tx: Transactions.ClaimAnchorOutputTx if !localCommitPublished.isConfirmed => tx.input.outPoint } watchSpentIfNeeded(commitTx, watchSpentQueue, irrevocablySpent) } @@ -294,7 +294,7 @@ trait ErrorHandlers extends CommonHandlers { def doPublish(remoteCommitPublished: RemoteCommitPublished, commitment: FullCommitment): Unit = { import remoteCommitPublished._ - val claimLocalAnchor = claimAnchorTxs.collect { case tx: Transactions.ClaimLocalAnchorOutputTx if !remoteCommitPublished.isConfirmed => PublishReplaceableTx(tx, commitment, commitTx) } + val claimLocalAnchor = claimAnchorTxs.collect { case tx: Transactions.ClaimAnchorOutputTx if !remoteCommitPublished.isConfirmed => PublishReplaceableTx(tx, commitment, commitTx) } val redeemableHtlcTxs = claimHtlcTxs.values.flatten.map(tx => PublishReplaceableTx(tx, commitment, commitTx)) val publishQueue = claimLocalAnchor ++ claimMainOutputTx.map(tx => PublishFinalTx(tx, tx.fee, None)).toSeq ++ redeemableHtlcTxs publishIfNeeded(publishQueue, irrevocablySpent) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunder.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunder.scala index 10301aaa8a..f76bd27bdd 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunder.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunder.scala @@ -94,7 +94,7 @@ object ReplaceableTxFunder { val maxFee = txInfo match { case tx: HtlcTx => tx.input.txOut.amount case tx: ClaimHtlcTx => tx.input.txOut.amount - case _: ClaimLocalAnchorOutputTx => + case _: ClaimAnchorOutputTx => val htlcBalance = commitment.localCommit.htlcTxsAndRemoteSigs.map(_.htlcTx.input.txOut.amount).sum val mainBalance = commitment.localCommit.spec.toLocal.truncateToSatoshi // If there are no HTLCs or a low HTLC amount, we still want to get back our main balance. @@ -110,7 +110,7 @@ object ReplaceableTxFunder { case _: ClaimHtlcSuccessTx => Transactions.claimHtlcSuccessWeight case _: LegacyClaimHtlcSuccessTx => Transactions.claimHtlcSuccessWeight case _: ClaimHtlcTimeoutTx => Transactions.claimHtlcTimeoutWeight - case _: ClaimLocalAnchorOutputTx => commitTx.weight() + Transactions.claimAnchorOutputMinWeight + case _: ClaimAnchorOutputTx => commitTx.weight() + Transactions.claimAnchorOutputMinWeight } // It doesn't make sense to use a feerate that is much higher than the current feerate for inclusion into the next block. Transactions.fee2rate(maxFee, weight).min(currentFeerates.fastest * 1.25) @@ -158,7 +158,7 @@ object ReplaceableTxFunder { def adjustPreviousTxOutput(previousTx: FundedTx, targetFeerate: FeeratePerKw, commitment: FullCommitment, commitTx: Transaction): AdjustPreviousTxOutputResult = { val dustLimit = commitment.localParams.dustLimit val targetFee = previousTx.signedTxWithWitnessData match { - case _: ClaimLocalAnchorWithWitnessData => + case _: ClaimAnchorWithWitnessData => val commitFee = commitment.localCommit.commitTxAndRemoteSig.commitTx.fee val totalWeight = previousTx.signedTx.weight() + commitTx.weight() weight2fee(targetFeerate, totalWeight) - commitFee @@ -166,7 +166,7 @@ object ReplaceableTxFunder { weight2fee(targetFeerate, previousTx.signedTx.weight()) } previousTx.signedTxWithWitnessData match { - case claimLocalAnchor: ClaimLocalAnchorWithWitnessData => + case claimLocalAnchor: ClaimAnchorWithWitnessData => val changeAmount = previousTx.totalAmountIn - targetFee if (changeAmount < dustLimit) { AdjustPreviousTxOutputResult.AddWalletInputs(claimLocalAnchor) @@ -233,7 +233,7 @@ private class ReplaceableTxFunder(nodeParams: NodeParams, def fund(txWithWitnessData: ReplaceableTxWithWitnessData, targetFeerate: FeeratePerKw): Behavior[Command] = { log.info("funding {} tx (targetFeerate={})", txWithWitnessData.txInfo.desc, targetFeerate) txWithWitnessData match { - case claimLocalAnchor: ClaimLocalAnchorWithWitnessData => + case claimLocalAnchor: ClaimAnchorWithWitnessData => val commitFeerate = cmd.commitment.localCommit.spec.commitTxFeerate if (targetFeerate <= commitFeerate) { log.info("skipping {}: commit feerate is high enough (feerate={})", cmd.desc, commitFeerate) @@ -312,7 +312,7 @@ private class ReplaceableTxFunder(nodeParams: NodeParams, private def sign(fundedTx: ReplaceableTxWithWitnessData, txFeerate: FeeratePerKw, amountIn: Satoshi, walletUtxos: Map[OutPoint, TxOut]): Behavior[Command] = { val channelKeyPath = keyManager.keyPath(cmd.commitment.localParams, cmd.commitment.params.channelConfig) fundedTx match { - case claimAnchorTx: ClaimLocalAnchorWithWitnessData => + case claimAnchorTx: ClaimAnchorWithWitnessData => val localSig = keyManager.sign(claimAnchorTx.txInfo, keyManager.fundingPublicKey(cmd.commitment.localParams.fundingKeyPath, cmd.commitment.fundingTxIndex), TxOwner.Local, cmd.commitment.params.commitmentFormat, walletUtxos) val signedTx = claimAnchorTx.copy(txInfo = addSigs(claimAnchorTx.txInfo, localSig)) signWalletInputs(signedTx, txFeerate, amountIn, walletUtxos) @@ -378,7 +378,7 @@ private class ReplaceableTxFunder(nodeParams: NodeParams, // For "claim anchor txs" there is a single change output that sends to our on-chain wallet. // For htlc txs the first output is the one we want to fund/bump, additional outputs send to our on-chain wallet. val ourWalletOutputs = locallySignedTx match { - case _: ClaimLocalAnchorWithWitnessData => Seq(0) + case _: ClaimAnchorWithWitnessData => Seq(0) case _: HtlcWithWitnessData => locallySignedTx.txInfo.tx.txOut.indices.tail } context.pipeToSelf(bitcoinClient.signPsbt(psbt1, ourWalletInputs, ourWalletOutputs)) { @@ -387,7 +387,7 @@ private class ReplaceableTxFunder(nodeParams: NodeParams, case Right(signedTx) => val actualFees = kmp2scala(processPsbtResponse.psbt.computeFees()) val actualWeight = locallySignedTx match { - case _: ClaimLocalAnchorWithWitnessData => signedTx.weight() + cmd.commitTx.weight() + case _: ClaimAnchorWithWitnessData => signedTx.weight() + cmd.commitTx.weight() case _ => signedTx.weight() } val actualFeerate = Transactions.fee2rate(actualFees, actualWeight) @@ -438,14 +438,14 @@ private class ReplaceableTxFunder(nodeParams: NodeParams, private def addInputs(tx: ReplaceableTxWithWalletInputs, targetFeerate: FeeratePerKw, commitment: FullCommitment): Future[(ReplaceableTxWithWalletInputs, Satoshi, Map[OutPoint, TxOut])] = { for { (fundedTx, amountIn) <- tx match { - case anchorTx: ClaimLocalAnchorWithWitnessData => addInputs(anchorTx, targetFeerate, commitment) + case anchorTx: ClaimAnchorWithWitnessData => addInputs(anchorTx, targetFeerate, commitment) case htlcTx: HtlcWithWitnessData => addInputs(htlcTx, targetFeerate, commitment) } spentUtxos <- getWalletUtxos(fundedTx.txInfo) } yield (fundedTx, amountIn, spentUtxos) } - private def addInputs(anchorTx: ClaimLocalAnchorWithWitnessData, targetFeerate: FeeratePerKw, commitment: FullCommitment): Future[(ClaimLocalAnchorWithWitnessData, Satoshi)] = { + private def addInputs(anchorTx: ClaimAnchorWithWitnessData, targetFeerate: FeeratePerKw, commitment: FullCommitment): Future[(ClaimAnchorWithWitnessData, Satoshi)] = { // We want to pay the commit fees using CPFP. Since the commit tx may not be in the mempool yet (its feerate may be // below the minimum acceptable mempool feerate), we cannot ask bitcoind to fund a transaction that spends that // commit tx: it would fail because it cannot find the input in the utxo set. So we instead ask bitcoind to fund an diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPrePublisher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPrePublisher.scala index e15119860e..7d91fa053c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPrePublisher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPrePublisher.scala @@ -70,8 +70,8 @@ object ReplaceableTxPrePublisher { sealed trait ReplaceableTxWithWalletInputs extends ReplaceableTxWithWitnessData { override def updateTx(tx: Transaction): ReplaceableTxWithWalletInputs } - case class ClaimLocalAnchorWithWitnessData(txInfo: ClaimLocalAnchorOutputTx) extends ReplaceableTxWithWalletInputs { - override def updateTx(tx: Transaction): ClaimLocalAnchorWithWitnessData = this.copy(txInfo = this.txInfo.copy(tx = tx)) + case class ClaimAnchorWithWitnessData(txInfo: ClaimAnchorOutputTx) extends ReplaceableTxWithWalletInputs { + override def updateTx(tx: Transaction): ClaimAnchorWithWitnessData = this.copy(txInfo = this.txInfo.copy(tx = tx)) } sealed trait HtlcWithWitnessData extends ReplaceableTxWithWalletInputs { override def txInfo: HtlcTx @@ -105,7 +105,7 @@ object ReplaceableTxPrePublisher { case CheckPreconditions(replyTo, cmd) => val prePublisher = new ReplaceableTxPrePublisher(nodeParams, replyTo, cmd, bitcoinClient, context) cmd.txInfo match { - case localAnchorTx: Transactions.ClaimLocalAnchorOutputTx => prePublisher.checkAnchorPreconditions(localAnchorTx) + case localAnchorTx: Transactions.ClaimAnchorOutputTx => prePublisher.checkAnchorPreconditions(localAnchorTx) case htlcTx: Transactions.HtlcTx => prePublisher.checkHtlcPreconditions(htlcTx) case claimHtlcTx: Transactions.ClaimHtlcTx => prePublisher.checkClaimHtlcPreconditions(claimHtlcTx) } @@ -126,7 +126,7 @@ private class ReplaceableTxPrePublisher(nodeParams: NodeParams, private val log = context.log - private def checkAnchorPreconditions(localAnchorTx: ClaimLocalAnchorOutputTx): Behavior[Command] = { + private def checkAnchorPreconditions(localAnchorTx: ClaimAnchorOutputTx): Behavior[Command] = { // We verify that: // - our commit is not confirmed (if it is, no need to claim our anchor) // - their commit is not confirmed (if it is, no need to claim our anchor either) @@ -162,7 +162,7 @@ private class ReplaceableTxPrePublisher(nodeParams: NodeParams, } Behaviors.receiveMessagePartial { case ParentTxOk => - replyTo ! PreconditionsOk(ClaimLocalAnchorWithWitnessData(localAnchorTx)) + replyTo ! PreconditionsOk(ClaimAnchorWithWitnessData(localAnchorTx)) Behaviors.stopped case FundingTxNotFound => log.debug("funding tx could not be found, we don't know yet if we need to claim our anchor") @@ -179,7 +179,7 @@ private class ReplaceableTxPrePublisher(nodeParams: NodeParams, case UnknownFailure(reason) => log.error(s"could not check ${cmd.desc} preconditions, proceeding anyway: ", reason) // If our checks fail, we don't want it to prevent us from trying to publish our commit tx. - replyTo ! PreconditionsOk(ClaimLocalAnchorWithWitnessData(localAnchorTx)) + replyTo ! PreconditionsOk(ClaimAnchorWithWitnessData(localAnchorTx)) Behaviors.stopped } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala index 340bd62d36..cbd79420e2 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala @@ -22,7 +22,7 @@ import fr.acinq.bitcoin.scalacompat.{SatoshiLong, Transaction} import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw, FeeratesPerKw} import fr.acinq.eclair.channel.publish.ReplaceableTxFunder.FundedTx -import fr.acinq.eclair.channel.publish.ReplaceableTxPrePublisher.{ClaimLocalAnchorWithWitnessData, ReplaceableTxWithWitnessData} +import fr.acinq.eclair.channel.publish.ReplaceableTxPrePublisher.{ClaimAnchorWithWitnessData, ReplaceableTxWithWitnessData} import fr.acinq.eclair.channel.publish.TxPublisher.TxPublishContext import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.{BlockHeight, NodeParams} @@ -118,7 +118,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, /** The confirmation target may be updated in some corner cases (e.g. for a htlc if we learn a payment preimage). */ private var confirmationTarget: ConfirmationTarget = cmd.txInfo.confirmationTarget - def checkPreconditions(): Behavior[Command] = { + private def checkPreconditions(): Behavior[Command] = { val prePublisher = context.spawn(ReplaceableTxPrePublisher(nodeParams, bitcoinClient, txPublishContext), "pre-publisher") prePublisher ! ReplaceableTxPrePublisher.CheckPreconditions(context.messageAdapter[ReplaceableTxPrePublisher.PreconditionsResult](WrappedPreconditionsResult), cmd) Behaviors.receiveMessagePartial { @@ -137,7 +137,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, def checkTimeLocks(txWithWitnessData: ReplaceableTxWithWitnessData): Behavior[Command] = { txWithWitnessData match { // There are no time locks on anchor transactions, we can claim them right away. - case _: ClaimLocalAnchorWithWitnessData => chooseFeerate(txWithWitnessData) + case _: ClaimAnchorWithWitnessData => chooseFeerate(txWithWitnessData) case _ => val timeLocksChecker = context.spawn(TxTimeLocksMonitor(nodeParams, bitcoinClient, txPublishContext), "time-locks-monitor") timeLocksChecker ! TxTimeLocksMonitor.CheckTx(context.messageAdapter[TxTimeLocksMonitor.TimeLocksOk](_ => TimeLocksOk), cmd.txInfo.tx, cmd.desc) @@ -151,7 +151,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, } } - def chooseFeerate(txWithWitnessData: ReplaceableTxWithWitnessData): Behavior[Command] = { + private def chooseFeerate(txWithWitnessData: ReplaceableTxWithWitnessData): Behavior[Command] = { context.pipeToSelf(hasEnoughSafeUtxos(nodeParams.onChainFeeConf.safeUtxosThreshold)) { case Success(isSafe) => CheckUtxosResult(isSafe, nodeParams.currentBlockHeight) case Failure(_) => CheckUtxosResult(isSafe = false, nodeParams.currentBlockHeight) // if we can't check our utxos, we assume the worst @@ -201,14 +201,14 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, // Wait for our transaction to be confirmed or rejected from the mempool. // If we get close to the confirmation target and our transaction is stuck in the mempool, we will initiate an RBF attempt. - def wait(tx: FundedTx): Behavior[Command] = { + private def wait(tx: FundedTx): Behavior[Command] = { Behaviors.receiveMessagePartial { case WrappedTxResult(txResult) => txResult match { case MempoolTxMonitor.TxInMempool(_, currentBlockHeight, parentConfirmed) => val shouldRbf = cmd.txInfo match { // Our commit tx was confirmed on its own, so there's no need to increase fees on the anchor tx. - case _: Transactions.ClaimLocalAnchorOutputTx if parentConfirmed => false + case _: Transactions.ClaimAnchorOutputTx if parentConfirmed => false case _ => true } if (shouldRbf) { @@ -259,7 +259,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, } // Fund a replacement transaction because our previous attempt seems to be stuck in the mempool. - def fundReplacement(targetFeerate: FeeratePerKw, previousTx: FundedTx): Behavior[Command] = { + private def fundReplacement(targetFeerate: FeeratePerKw, previousTx: FundedTx): Behavior[Command] = { log.info("bumping {} fees: previous feerate={}, next feerate={}", cmd.desc, previousTx.feerate, targetFeerate) val txFunder = context.spawn(ReplaceableTxFunder(nodeParams, bitcoinClient, txPublishContext), "tx-funder-rbf") txFunder ! ReplaceableTxFunder.FundTransaction(context.messageAdapter[ReplaceableTxFunder.FundingResult](WrappedFundingResult), cmd, Left(previousTx), targetFeerate) @@ -290,7 +290,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, // Publish an RBF attempt. We then have two concurrent transactions: the previous one and the updated one. // Only one of them can be in the mempool, so we wait for the other to be rejected. Once that's done, we're back to a // situation where we have one transaction in the mempool and wait for it to confirm. - def publishReplacement(previousTx: FundedTx, bumpedTx: FundedTx): Behavior[Command] = { + private def publishReplacement(previousTx: FundedTx, bumpedTx: FundedTx): Behavior[Command] = { val txMonitor = context.spawn(MempoolTxMonitor(nodeParams, bitcoinClient, txPublishContext), s"mempool-tx-monitor-${bumpedTx.signedTx.txid}") val parentTx_opt = cmd.txInfo match { // Anchor output transactions are packaged with the corresponding commitment transaction. @@ -333,7 +333,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, } // Clean up the failed transaction attempt. Once that's done, go back to the waiting state with the new transaction. - def cleanUpFailedTxAndWait(failedTx: Transaction, mempoolTx: FundedTx): Behavior[Command] = { + private def cleanUpFailedTxAndWait(failedTx: Transaction, mempoolTx: FundedTx): Behavior[Command] = { // Note that we don't need to filter inputs from the previous transaction, they have already been unlocked when the // previous transaction was published (but the bitcoin wallet ensures that they won't be double-spent). val toUnlock = failedTx.txIn.map(_.outPoint).toSet diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/TxPublisher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/TxPublisher.scala index cd60df31d8..d239260b44 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/TxPublisher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/TxPublisher.scala @@ -25,7 +25,7 @@ import fr.acinq.eclair.blockchain.CurrentBlockHeight import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient import fr.acinq.eclair.blockchain.fee.ConfirmationTarget import fr.acinq.eclair.channel.FullCommitment -import fr.acinq.eclair.transactions.Transactions.{ClaimLocalAnchorOutputTx, ReplaceableTransactionWithInputInfo, TransactionWithInputInfo} +import fr.acinq.eclair.transactions.Transactions.{ClaimAnchorOutputTx, ReplaceableTransactionWithInputInfo, TransactionWithInputInfo} import fr.acinq.eclair.{BlockHeight, Logs, NodeParams} import java.util.UUID @@ -99,8 +99,8 @@ object TxPublisher { override def desc: String = txInfo.desc /** True if we're trying to bump our local commit with an anchor transaction. */ - lazy val isLocalCommitAnchor = txInfo match { - case txInfo: ClaimLocalAnchorOutputTx => txInfo.input.outPoint.txid == commitment.localCommit.commitTxAndRemoteSig.commitTx.tx.txid + lazy val isLocalCommitAnchor: Boolean = txInfo match { + case txInfo: ClaimAnchorOutputTx => txInfo.input.outPoint.txid == commitment.localCommit.commitTxAndRemoteSig.commitTx.tx.txid case _ => false } } @@ -136,7 +136,7 @@ object TxPublisher { // @formatter:on // @formatter:off - case class ChannelContext(remoteNodeId: PublicKey, channelId_opt: Option[ByteVector32]) { + private case class ChannelContext(remoteNodeId: PublicKey, channelId_opt: Option[ByteVector32]) { def mdc(): Map[String, String] = Logs.mdc(remoteNodeId_opt = Some(remoteNodeId), channelId_opt = channelId_opt) } case class TxPublishContext(id: UUID, remoteNodeId: PublicKey, channelId_opt: Option[ByteVector32]) { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Scripts.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Scripts.scala index 6137d9e7f0..be4b6ca386 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Scripts.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Scripts.scala @@ -28,6 +28,7 @@ import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta} import scodec.bits.ByteVector import scala.jdk.CollectionConverters.SeqHasAsJava +import scala.util.{Success, Try} /** * Created by PM on 02/12/2016. @@ -70,13 +71,34 @@ object Scripts { * @param n input number * @return a script element that represents n */ - def encodeNumber(n: Long): ScriptElt = n match { + private def encodeNumber(n: Long): ScriptElt = n match { case 0 => OP_0 case -1 => OP_1NEGATE case x if x >= 1 && x <= 16 => ScriptElt.code2elt((ScriptElt.elt2code(OP_1) + x - 1).toInt).get case _ => OP_PUSHDATA(Script.encodeNumber(n)) } + /** As defined in https://github.com/lightning/bolts/blob/master/03-transactions.md#dust-limits */ + def dustLimit(scriptPubKey: ByteVector): Satoshi = { + Try(Script.parse(scriptPubKey)) match { + case Success(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(pubkeyHash, _) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) if pubkeyHash.size == 20 => 546.sat + case Success(OP_HASH160 :: OP_PUSHDATA(scriptHash, _) :: OP_EQUAL :: Nil) if scriptHash.size == 20 => 540.sat + case Success(OP_0 :: OP_PUSHDATA(pubkeyHash, _) :: Nil) if pubkeyHash.size == 20 => 294.sat + case Success(OP_0 :: OP_PUSHDATA(scriptHash, _) :: Nil) if scriptHash.size == 32 => 330.sat + case Success((OP_1 | OP_2 | OP_3 | OP_4 | OP_5 | OP_6 | OP_7 | OP_8 | OP_9 | OP_10 | OP_11 | OP_12 | OP_13 | OP_14 | OP_15 | OP_16) :: OP_PUSHDATA(program, _) :: Nil) if 2 <= program.length && program.length <= 40 => 354.sat + case Success(OP_RETURN :: _) => 0.sat // OP_RETURN is never dust + case _ => 546.sat + } + } + + /** Checks if the given script is an OP_RETURN. */ + def isOpReturn(scriptPubKey: ByteVector): Boolean = { + Try(Script.parse(scriptPubKey)) match { + case Success(OP_RETURN :: _) => true + case _ => false + } + } + /** * This function interprets the locktime for the given transaction, and returns the block height before which this tx cannot be published. * By convention in bitcoin, depending of the value of locktime it might be a number of blocks or a number of seconds since epoch. @@ -89,8 +111,7 @@ object Scripts { if (tx.lockTime <= LOCKTIME_THRESHOLD) { // locktime is a number of blocks BlockHeight(tx.lockTime) - } - else { + } else { // locktime is a unix epoch timestamp require(tx.lockTime <= 0x20FFFFFF, "locktime should be lesser than 0x20FFFFFF") // since locktime is very well in the past (0x20FFFFFF is in 1987), it is equivalent to no locktime at all @@ -140,14 +161,14 @@ object Scripts { /** * This witness script spends a [[toLocalDelayed]] output using a local sig after a delay */ - def witnessToLocalDelayedAfterDelay(localSig: ByteVector64, toLocalDelayedScript: ByteVector) = + def witnessToLocalDelayedAfterDelay(localSig: ByteVector64, toLocalDelayedScript: ByteVector): ScriptWitness = ScriptWitness(der(localSig) :: ByteVector.empty :: toLocalDelayedScript :: Nil) /** * This witness script spends (steals) a [[toLocalDelayed]] output using a revocation key as a punishment * for having published a revoked transaction */ - def witnessToLocalDelayedWithRevocationSig(revocationSig: ByteVector64, toLocalScript: ByteVector) = + def witnessToLocalDelayedWithRevocationSig(revocationSig: ByteVector64, toLocalScript: ByteVector): ScriptWitness = ScriptWitness(der(revocationSig) :: ByteVector(1) :: toLocalScript :: Nil) /** @@ -161,7 +182,7 @@ object Scripts { * If remote publishes its commit tx where there was a to_remote delayed output (anchor outputs format), then local * uses this script to claim its funds (consumes to_remote script from commit tx). */ - def witnessClaimToRemoteDelayedFromCommitTx(localSig: ByteVector64, toRemoteDelayedScript: ByteVector) = + def witnessClaimToRemoteDelayedFromCommitTx(localSig: ByteVector64, toRemoteDelayedScript: ByteVector): ScriptWitness = ScriptWitness(der(localSig) :: toRemoteDelayedScript :: Nil) /** @@ -180,12 +201,7 @@ object Scripts { /** * This witness script spends a local [[anchor]] output using a local sig. */ - def witnessAnchor(localSig: ByteVector64, anchorScript: ByteVector) = ScriptWitness(der(localSig) :: anchorScript :: Nil) - - /** - * This witness script spends either a local or remote [[anchor]] output after its CSV delay. - */ - def witnessAnchorAfterDelay(anchorScript: ByteVector) = ScriptWitness(ByteVector.empty :: anchorScript :: Nil) + def witnessAnchor(localSig: ByteVector64, anchorScript: ByteVector): ScriptWitness = ScriptWitness(der(localSig) :: anchorScript :: Nil) def htlcOffered(localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, revocationPubKey: PublicKey, paymentHash: ByteVector, commitmentFormat: CommitmentFormat): Seq[ScriptElt] = { val addCsvDelay = commitmentFormat match { @@ -218,7 +234,7 @@ object Scripts { /** * This is the witness script of the 2nd-stage HTLC Success transaction (consumes htlcOffered script from commit tx) */ - def witnessHtlcSuccess(localSig: ByteVector64, remoteSig: ByteVector64, paymentPreimage: ByteVector32, htlcOfferedScript: ByteVector, commitmentFormat: CommitmentFormat) = + def witnessHtlcSuccess(localSig: ByteVector64, remoteSig: ByteVector64, paymentPreimage: ByteVector32, htlcOfferedScript: ByteVector, commitmentFormat: CommitmentFormat): ScriptWitness = ScriptWitness(ByteVector.empty :: der(remoteSig, htlcRemoteSighash(commitmentFormat)) :: der(localSig) :: paymentPreimage.bytes :: htlcOfferedScript :: Nil) /** Extract the payment preimage from a 2nd-stage HTLC Success transaction's witness script */ @@ -233,7 +249,7 @@ object Scripts { * If remote publishes its commit tx where there was a remote->local htlc, then local uses this script to * claim its funds using a payment preimage (consumes htlcOffered script from commit tx) */ - def witnessClaimHtlcSuccessFromCommitTx(localSig: ByteVector64, paymentPreimage: ByteVector32, htlcOffered: ByteVector) = + def witnessClaimHtlcSuccessFromCommitTx(localSig: ByteVector64, paymentPreimage: ByteVector32, htlcOffered: ByteVector): ScriptWitness = ScriptWitness(der(localSig) :: paymentPreimage.bytes :: htlcOffered :: Nil) /** Extract the payment preimage from from a fulfilled offered htlc. */ @@ -277,21 +293,21 @@ object Scripts { /** * This is the witness script of the 2nd-stage HTLC Timeout transaction (consumes htlcOffered script from commit tx) */ - def witnessHtlcTimeout(localSig: ByteVector64, remoteSig: ByteVector64, htlcOfferedScript: ByteVector, commitmentFormat: CommitmentFormat) = + def witnessHtlcTimeout(localSig: ByteVector64, remoteSig: ByteVector64, htlcOfferedScript: ByteVector, commitmentFormat: CommitmentFormat): ScriptWitness = ScriptWitness(ByteVector.empty :: der(remoteSig, htlcRemoteSighash(commitmentFormat)) :: der(localSig) :: ByteVector.empty :: htlcOfferedScript :: Nil) /** * If remote publishes its commit tx where there was a local->remote htlc, then local uses this script to * claim its funds after timeout (consumes htlcReceived script from commit tx) */ - def witnessClaimHtlcTimeoutFromCommitTx(localSig: ByteVector64, htlcReceivedScript: ByteVector) = + def witnessClaimHtlcTimeoutFromCommitTx(localSig: ByteVector64, htlcReceivedScript: ByteVector): ScriptWitness = ScriptWitness(der(localSig) :: ByteVector.empty :: htlcReceivedScript :: Nil) /** * This witness script spends (steals) a [[htlcOffered]] or [[htlcReceived]] output using a revocation key as a punishment * for having published a revoked transaction */ - def witnessHtlcWithRevocationSig(revocationSig: ByteVector64, revocationPubkey: PublicKey, htlcScript: ByteVector) = + def witnessHtlcWithRevocationSig(revocationSig: ByteVector64, revocationPubkey: PublicKey, htlcScript: ByteVector): ScriptWitness = ScriptWitness(der(revocationSig) :: revocationPubkey.value :: htlcScript :: Nil) /** @@ -324,7 +340,7 @@ object Scripts { /** * "Nothing Up My Sleeve" point, for which there is no known private key. */ - val NUMS_POINT = PublicKey(ByteVector.fromValidHex("02dca094751109d0bd055d03565874e8276dd53e926b44e3bd1bb6bf4bc130a279")) + val NUMS_POINT: PublicKey = PublicKey(ByteVector.fromValidHex("02dca094751109d0bd055d03565874e8276dd53e926b44e3bd1bb6bf4bc130a279")) // miniscript: older(16) private val anchorScript: Seq[ScriptElt] = OP_16 :: OP_CHECKSEQUENCEVERIFY :: Nil diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala index 9cb1ea9194..897aca9e16 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.wire.protocol.UpdateAddHtlc import scodec.bits.ByteVector import java.nio.ByteOrder -import scala.util.{Success, Try} +import scala.util.Try /** * Created by PM on 15/12/2016. @@ -76,7 +76,7 @@ object Transactions { } object AnchorOutputsCommitmentFormat { - val anchorAmount = Satoshi(330) + val anchorAmount: Satoshi = Satoshi(330) } /** @@ -213,9 +213,7 @@ object Transactions { case class LegacyClaimHtlcSuccessTx(input: InputInfo, tx: Transaction, htlcId: Long, confirmationTarget: ConfirmationTarget.Absolute) extends ClaimHtlcTx { override def desc: String = "claim-htlc-success" } case class ClaimHtlcSuccessTx(input: InputInfo, tx: Transaction, paymentHash: ByteVector32, htlcId: Long, confirmationTarget: ConfirmationTarget.Absolute) extends ClaimHtlcTx { override def desc: String = "claim-htlc-success" } case class ClaimHtlcTimeoutTx(input: InputInfo, tx: Transaction, htlcId: Long, confirmationTarget: ConfirmationTarget.Absolute) extends ClaimHtlcTx { override def desc: String = "claim-htlc-timeout" } - sealed trait ClaimAnchorOutputTx extends TransactionWithInputInfo - case class ClaimLocalAnchorOutputTx(input: InputInfo, tx: Transaction, confirmationTarget: ConfirmationTarget) extends ClaimAnchorOutputTx with ReplaceableTransactionWithInputInfo { override def desc: String = "local-anchor" } - case class ClaimRemoteAnchorOutputTx(input: InputInfo, tx: Transaction) extends ClaimAnchorOutputTx { override def desc: String = "remote-anchor" } + case class ClaimAnchorOutputTx(input: InputInfo, tx: Transaction, confirmationTarget: ConfirmationTarget) extends ReplaceableTransactionWithInputInfo { override def desc: String = "local-anchor" } sealed trait ClaimRemoteCommitMainOutputTx extends TransactionWithInputInfo case class ClaimP2WPKHOutputTx(input: InputInfo, tx: Transaction) extends ClaimRemoteCommitMainOutputTx { override def desc: String = "remote-main" } case class ClaimRemoteDelayedOutputTx(input: InputInfo, tx: Transaction) extends ClaimRemoteCommitMainOutputTx { override def desc: String = "remote-main-delayed" } @@ -233,7 +231,7 @@ object Transactions { /** * When *local* *current* [[CommitTx]] is published: * - [[ClaimLocalDelayedOutputTx]] spends to-local output of [[CommitTx]] after a delay - * - When using anchor outputs, [[ClaimLocalAnchorOutputTx]] spends to-local anchor of [[CommitTx]] + * - When using anchor outputs, [[ClaimAnchorOutputTx]] spends to-local anchor of [[CommitTx]] * - [[HtlcSuccessTx]] spends htlc-received outputs of [[CommitTx]] for which we have the preimage * - [[HtlcDelayedTx]] spends [[HtlcSuccessTx]] after a delay * - [[HtlcTimeoutTx]] spends htlc-sent outputs of [[CommitTx]] after a timeout @@ -242,14 +240,14 @@ object Transactions { * When *remote* *current* [[CommitTx]] is published: * - When using the default commitment format, [[ClaimP2WPKHOutputTx]] spends to-local output of [[CommitTx]] * - When using anchor outputs, [[ClaimRemoteDelayedOutputTx]] spends to-local output of [[CommitTx]] - * - When using anchor outputs, [[ClaimLocalAnchorOutputTx]] spends to-local anchor of [[CommitTx]] + * - When using anchor outputs, [[ClaimAnchorOutputTx]] spends to-local anchor of [[CommitTx]] * - [[ClaimHtlcSuccessTx]] spends htlc-received outputs of [[CommitTx]] for which we have the preimage * - [[ClaimHtlcTimeoutTx]] spends htlc-sent outputs of [[CommitTx]] after a timeout * * When *remote* *revoked* [[CommitTx]] is published: * - When using the default commitment format, [[ClaimP2WPKHOutputTx]] spends to-local output of [[CommitTx]] * - When using anchor outputs, [[ClaimRemoteDelayedOutputTx]] spends to-local output of [[CommitTx]] - * - When using anchor outputs, [[ClaimLocalAnchorOutputTx]] spends to-local anchor of [[CommitTx]] + * - When using anchor outputs, [[ClaimAnchorOutputTx]] spends to-local anchor of [[CommitTx]] * - [[MainPenaltyTx]] spends remote main output using the per-commitment secret * - [[HtlcSuccessTx]] spends htlc-sent outputs of [[CommitTx]] for which they have the preimage (published by remote) * - [[ClaimHtlcDelayedOutputPenaltyTx]] spends [[HtlcSuccessTx]] using the revocation secret (published by local) @@ -273,7 +271,7 @@ object Transactions { val mainPenaltyWeight = 484 val htlcPenaltyWeight = 578 // based on spending an HTLC-Success output (would be 571 with HTLC-Timeout) - def weight2feeMsat(feeratePerKw: FeeratePerKw, weight: Int): MilliSatoshi = MilliSatoshi(feeratePerKw.toLong * weight) + private def weight2feeMsat(feeratePerKw: FeeratePerKw, weight: Int): MilliSatoshi = MilliSatoshi(feeratePerKw.toLong * weight) def weight2fee(feeratePerKw: FeeratePerKw, weight: Int): Satoshi = weight2feeMsat(feeratePerKw, weight).truncateToSatoshi @@ -284,27 +282,6 @@ object Transactions { */ def fee2rate(fee: Satoshi, weight: Int): FeeratePerKw = FeeratePerKw((fee * 1000L) / weight) - /** As defined in https://github.com/lightning/bolts/blob/master/03-transactions.md#dust-limits */ - def dustLimit(scriptPubKey: ByteVector): Satoshi = { - Try(Script.parse(scriptPubKey)) match { - case Success(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(pubkeyHash, _) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) if pubkeyHash.size == 20 => 546.sat - case Success(OP_HASH160 :: OP_PUSHDATA(scriptHash, _) :: OP_EQUAL :: Nil) if scriptHash.size == 20 => 540.sat - case Success(OP_0 :: OP_PUSHDATA(pubkeyHash, _) :: Nil) if pubkeyHash.size == 20 => 294.sat - case Success(OP_0 :: OP_PUSHDATA(scriptHash, _) :: Nil) if scriptHash.size == 32 => 330.sat - case Success((OP_1 | OP_2 | OP_3 | OP_4 | OP_5 | OP_6 | OP_7 | OP_8 | OP_9 | OP_10 | OP_11 | OP_12 | OP_13 | OP_14 | OP_15 | OP_16) :: OP_PUSHDATA(program, _) :: Nil) if 2 <= program.length && program.length <= 40 => 354.sat - case Success(OP_RETURN :: _) => 0.sat // OP_RETURN is never dust - case _ => 546.sat - } - } - - /** When an output is using OP_RETURN, we usually want to make sure its amount is 0, otherwise bitcoind won't accept it. */ - def isOpReturn(scriptPubKey: ByteVector): Boolean = { - Try(Script.parse(scriptPubKey)) match { - case Success(OP_RETURN :: _) => true - case _ => false - } - } - /** Offered HTLCs below this amount will be trimmed. */ def offeredHtlcTrimThreshold(dustLimit: Satoshi, spec: CommitmentSpec, commitmentFormat: CommitmentFormat): Satoshi = dustLimit + weight2fee(spec.htlcTxFeerate(commitmentFormat), commitmentFormat.htlcTimeoutWeight) @@ -417,7 +394,7 @@ object Transactions { def decodeTxNumber(sequence: Long, locktime: Long): Long = ((sequence & 0xffffffL) << 24) + (locktime & 0xffffffL) - def getHtlcTxInputSequence(commitmentFormat: CommitmentFormat): Long = commitmentFormat match { + private def getHtlcTxInputSequence(commitmentFormat: CommitmentFormat): Long = commitmentFormat match { case DefaultCommitmentFormat => 0 // htlc txs immediately spend the commit tx case _: AnchorOutputsCommitmentFormat => 1 // htlc txs have a 1-block delay to allow CPFP carve-out on anchors } @@ -432,7 +409,7 @@ object Transactions { case class CommitmentOutputLink[T <: CommitmentOutput](output: TxOut, redeemScript: Seq[ScriptElt], commitmentOutput: T) /** Type alias for a collection of commitment output links */ - type CommitmentOutputs = Seq[CommitmentOutputLink[CommitmentOutput]] + private type CommitmentOutputs = Seq[CommitmentOutputLink[CommitmentOutput]] object CommitmentOutputLink { /** @@ -531,15 +508,15 @@ object Transactions { CommitTx(commitTxInput, tx) } - def makeHtlcTimeoutTx(commitTx: Transaction, - output: CommitmentOutputLink[OutHtlc], - outputIndex: Int, - localDustLimit: Satoshi, - localRevocationPubkey: PublicKey, - toLocalDelay: CltvExpiryDelta, - localDelayedPaymentPubkey: PublicKey, - feeratePerKw: FeeratePerKw, - commitmentFormat: CommitmentFormat): Either[TxGenerationSkipped, HtlcTimeoutTx] = { + private def makeHtlcTimeoutTx(commitTx: Transaction, + output: CommitmentOutputLink[OutHtlc], + outputIndex: Int, + localDustLimit: Satoshi, + localRevocationPubkey: PublicKey, + toLocalDelay: CltvExpiryDelta, + localDelayedPaymentPubkey: PublicKey, + feeratePerKw: FeeratePerKw, + commitmentFormat: CommitmentFormat): Either[TxGenerationSkipped, HtlcTimeoutTx] = { val fee = weight2fee(feeratePerKw, commitmentFormat.htlcTimeoutWeight) val redeemScript = output.redeemScript val htlc = output.commitmentOutput.outgoingHtlc.add @@ -558,15 +535,15 @@ object Transactions { } } - def makeHtlcSuccessTx(commitTx: Transaction, - output: CommitmentOutputLink[InHtlc], - outputIndex: Int, - localDustLimit: Satoshi, - localRevocationPubkey: PublicKey, - toLocalDelay: CltvExpiryDelta, - localDelayedPaymentPubkey: PublicKey, - feeratePerKw: FeeratePerKw, - commitmentFormat: CommitmentFormat): Either[TxGenerationSkipped, HtlcSuccessTx] = { + private def makeHtlcSuccessTx(commitTx: Transaction, + output: CommitmentOutputLink[InHtlc], + outputIndex: Int, + localDustLimit: Satoshi, + localRevocationPubkey: PublicKey, + toLocalDelay: CltvExpiryDelta, + localDelayedPaymentPubkey: PublicKey, + feeratePerKw: FeeratePerKw, + commitmentFormat: CommitmentFormat): Either[TxGenerationSkipped, HtlcSuccessTx] = { val fee = weight2fee(feeratePerKw, commitmentFormat.htlcSuccessWeight) val redeemScript = output.redeemScript val htlc = output.commitmentOutput.incomingHtlc.add @@ -766,7 +743,7 @@ object Transactions { } } - private def makeClaimAnchorOutputTx(commitTx: Transaction, fundingPubkey: PublicKey): Either[TxGenerationSkipped, (InputInfo, Transaction)] = { + def makeClaimAnchorOutputTx(commitTx: Transaction, fundingPubkey: PublicKey, confirmationTarget: ConfirmationTarget): Either[TxGenerationSkipped, ClaimAnchorOutputTx] = { val redeemScript = anchor(fundingPubkey) val pubkeyScript = write(pay2wsh(redeemScript)) findPubKeyScriptIndex(commitTx, pubkeyScript) match { @@ -779,18 +756,10 @@ object Transactions { txIn = TxIn(input.outPoint, ByteVector.empty, 0) :: Nil, txOut = Nil, // anchor is only used to bump fees, the output will be added later depending on available inputs lockTime = 0) - Right((input, tx)) + Right(ClaimAnchorOutputTx(input, tx, confirmationTarget)) } } - def makeClaimLocalAnchorOutputTx(commitTx: Transaction, localFundingPubkey: PublicKey, confirmationTarget: ConfirmationTarget): Either[TxGenerationSkipped, ClaimLocalAnchorOutputTx] = { - makeClaimAnchorOutputTx(commitTx, localFundingPubkey).map { case (input, tx) => ClaimLocalAnchorOutputTx(input, tx, confirmationTarget) } - } - - def makeClaimRemoteAnchorOutputTx(commitTx: Transaction, remoteFundingPubkey: PublicKey): Either[TxGenerationSkipped, ClaimRemoteAnchorOutputTx] = { - makeClaimAnchorOutputTx(commitTx, remoteFundingPubkey).map { case (input, tx) => ClaimRemoteAnchorOutputTx(input, tx) } - } - def makeClaimHtlcDelayedOutputPenaltyTxs(htlcTx: Transaction, localDustLimit: Satoshi, localRevocationPubkey: PublicKey, toLocalDelay: CltvExpiryDelta, localDelayedPaymentPubkey: PublicKey, localFinalScriptPubKey: ByteVector, feeratePerKw: FeeratePerKw): Seq[Either[TxGenerationSkipped, ClaimHtlcDelayedOutputPenaltyTx]] = { val redeemScript = toLocalDelayed(localRevocationPubkey, toLocalDelay, localDelayedPaymentPubkey) val pubkeyScript = write(pay2wsh(redeemScript)) @@ -918,7 +887,7 @@ object Transactions { case SimpleClosingTxFee.PaidByThem(fee) => (spec.toLocal.truncateToSatoshi, spec.toRemote.truncateToSatoshi - fee) } - // An OP_RETURN script may be provided, but only when burning all of the peer's balance to fees. + // An OP_RETURN script may be provided, but only when burning all of the peer's balance to fees, otherwise bitcoind won't accept it. val toLocalOutput_opt = if (toLocalAmount >= dustLimit(localScriptPubKey)) { val amount = if (isOpReturn(localScriptPubKey)) 0.sat else toLocalAmount Some(TxOut(amount, localScriptPubKey)) @@ -968,7 +937,7 @@ object Transactions { } } - def findPubKeyScriptIndexes(tx: Transaction, pubkeyScript: ByteVector): Either[TxGenerationSkipped, Seq[Int]] = { + private def findPubKeyScriptIndexes(tx: Transaction, pubkeyScript: ByteVector): Either[TxGenerationSkipped, Seq[Int]] = { val outputIndexes = tx.txOut.zipWithIndex.collect { case (txOut, index) if txOut.publicKeyScript == pubkeyScript => index } @@ -982,13 +951,13 @@ object Transactions { /** * Default public key used for fee estimation */ - val PlaceHolderPubKey = PrivateKey(ByteVector32.One).publicKey + val PlaceHolderPubKey: PublicKey = PrivateKey(ByteVector32.One).publicKey /** * This default sig takes 72B when encoded in DER (incl. 1B for the trailing sig hash), it is used for fee estimation * It is 72 bytes because our signatures are normalized (low-s) and will take up 72 bytes at most in DER format */ - val PlaceHolderSig = ByteVector64(ByteVector.fill(64)(0xaa)) + val PlaceHolderSig: ByteVector64 = ByteVector64(ByteVector.fill(64)(0xaa)) assert(der(PlaceHolderSig).size == 72) def addSigs(commitTx: CommitTx, localFundingPubkey: PublicKey, remoteFundingPubkey: PublicKey, localSig: ByteVector64, remoteSig: ByteVector64): CommitTx = { @@ -1064,7 +1033,7 @@ object Transactions { case _: InputInfo.TaprootInput => htlcDelayedTx } - def addSigs(claimAnchorOutputTx: ClaimLocalAnchorOutputTx, localSig: ByteVector64): ClaimLocalAnchorOutputTx = claimAnchorOutputTx.input match { + def addSigs(claimAnchorOutputTx: ClaimAnchorOutputTx, localSig: ByteVector64): ClaimAnchorOutputTx = claimAnchorOutputTx.input match { case InputInfo.SegwitInput(_, _, redeemScript) => val witness = witnessAnchor(localSig, redeemScript) claimAnchorOutputTx.copy(tx = claimAnchorOutputTx.tx.updateWitness(0, witness)) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version2/ChannelCodecs2.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version2/ChannelCodecs2.scala index 93d405f7cf..37447262ca 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version2/ChannelCodecs2.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version2/ChannelCodecs2.scala @@ -124,8 +124,9 @@ private[channel] object ChannelCodecs2 { val mainPenaltyTxCodec: Codec[MainPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[MainPenaltyTx] val htlcPenaltyTxCodec: Codec[HtlcPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[HtlcPenaltyTx] val claimHtlcDelayedOutputPenaltyTxCodec: Codec[ClaimHtlcDelayedOutputPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[ClaimHtlcDelayedOutputPenaltyTx] - val claimLocalAnchorOutputTxCodec: Codec[ClaimLocalAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | defaultConfirmationTarget.upcast[ConfirmationTarget])).as[ClaimLocalAnchorOutputTx] - val claimRemoteAnchorOutputTxCodec: Codec[ClaimRemoteAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[ClaimRemoteAnchorOutputTx] + val claimLocalAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | defaultConfirmationTarget.upcast[ConfirmationTarget])).as[ClaimAnchorOutputTx] + // We previously created an unused transaction spending the remote anchor (after the 16-blocks delay). + val unusedRemoteAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: provide(ConfirmationTarget.Absolute(BlockHeight(42))).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] val closingTxCodec: Codec[ClosingTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("outputIndex" | optional(bool8, outputInfoCodec))).as[ClosingTx] val claimRemoteCommitMainOutputTxCodec: Codec[ClaimRemoteCommitMainOutputTx] = discriminated[ClaimRemoteCommitMainOutputTx].by(uint8) @@ -134,7 +135,9 @@ private[channel] object ChannelCodecs2 { val claimAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = discriminated[ClaimAnchorOutputTx].by(uint8) .typecase(0x01, claimLocalAnchorOutputTxCodec) - .typecase(0x02, claimRemoteAnchorOutputTxCodec) + .typecase(0x02, unusedRemoteAnchorOutputTxCodec) + + def filterUnusedRemoteAnchorOutputTx(anchorTxs: List[ClaimAnchorOutputTx]): List[ClaimAnchorOutputTx] = anchorTxs.filter(_.confirmationTarget != ConfirmationTarget.Absolute(BlockHeight(42))) val htlcTxCodec: Codec[HtlcTx] = discriminated[HtlcTx].by(uint8) .typecase(0x01, htlcSuccessTxCodec) @@ -243,14 +246,14 @@ private[channel] object ChannelCodecs2 { ("claimMainDelayedOutputTx" | optional(bool8, claimLocalDelayedOutputTxCodec)) :: ("htlcTxs" | mapCodec(outPointCodec, optional(bool8, htlcTxCodec))) :: ("claimHtlcDelayedTx" | listOfN(uint16, htlcDelayedTxCodec)) :: - ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec)) :: + ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec).xmap(txs => filterUnusedRemoteAnchorOutputTx(txs), { txs: List[ClaimAnchorOutputTx] => txs })) :: ("spent" | spentMapCodec)).as[LocalCommitPublished] val remoteCommitPublishedCodec: Codec[RemoteCommitPublished] = ( ("commitTx" | txCodec) :: ("claimMainOutputTx" | optional(bool8, claimRemoteCommitMainOutputTxCodec)) :: ("claimHtlcTxs" | mapCodec(outPointCodec, optional(bool8, claimHtlcTxCodec))) :: - ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec)) :: + ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec).xmap(txs => filterUnusedRemoteAnchorOutputTx(txs), { txs: List[ClaimAnchorOutputTx] => txs })) :: ("spent" | spentMapCodec)).as[RemoteCommitPublished] val revokedCommitPublishedCodec: Codec[RevokedCommitPublished] = ( diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3.scala index e9aa69650e..f9e0313aef 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3.scala @@ -143,9 +143,10 @@ private[channel] object ChannelCodecs3 { val mainPenaltyTxCodec: Codec[MainPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[MainPenaltyTx] val htlcPenaltyTxCodec: Codec[HtlcPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[HtlcPenaltyTx] val claimHtlcDelayedOutputPenaltyTxCodec: Codec[ClaimHtlcDelayedOutputPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[ClaimHtlcDelayedOutputPenaltyTx] - val claimLocalAnchorOutputTxCodec: Codec[ClaimLocalAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | blockHeightConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimLocalAnchorOutputTx] - private val claimLocalAnchorOutputTxNoConfirmCodec: Codec[ClaimLocalAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | defaultConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimLocalAnchorOutputTx] - val claimRemoteAnchorOutputTxCodec: Codec[ClaimRemoteAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[ClaimRemoteAnchorOutputTx] + val claimLocalAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | blockHeightConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] + private val claimLocalAnchorOutputTxNoConfirmCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | defaultConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] + // We previously created an unused transaction spending the remote anchor (after the 16-blocks delay). + val unusedRemoteAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: provide(ConfirmationTarget.Absolute(BlockHeight(42))).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] val closingTxCodec: Codec[ClosingTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("outputIndex" | optional(bool8, outputInfoCodec))).as[ClosingTx] val txWithInputInfoCodec: Codec[TransactionWithInputInfo] = discriminated[TransactionWithInputInfo].by(uint16) @@ -166,7 +167,7 @@ private[channel] object ChannelCodecs3 { .typecase(0x09, htlcPenaltyTxCodec) .typecase(0x10, closingTxCodec) .typecase(0x11, claimLocalAnchorOutputTxNoConfirmCodec) - .typecase(0x12, claimRemoteAnchorOutputTxCodec) + .typecase(0x12, unusedRemoteAnchorOutputTxCodec) .typecase(0x13, claimRemoteDelayedOutputTxCodec) .typecase(0x14, claimHtlcDelayedOutputPenaltyTxCodec) .typecase(0x15, htlcDelayedTxCodec) @@ -180,7 +181,9 @@ private[channel] object ChannelCodecs3 { // Important: order matters! .typecase(0x11, claimLocalAnchorOutputTxCodec) .typecase(0x01, claimLocalAnchorOutputTxNoConfirmCodec) - .typecase(0x02, claimRemoteAnchorOutputTxCodec) + .typecase(0x02, unusedRemoteAnchorOutputTxCodec) + + def filterUnusedRemoteAnchorOutputTx(anchorTxs: List[ClaimAnchorOutputTx]): List[ClaimAnchorOutputTx] = anchorTxs.filter(_.confirmationTarget != ConfirmationTarget.Absolute(BlockHeight(42))) val htlcTxCodec: Codec[HtlcTx] = discriminated[HtlcTx].by(uint8) // Important: order matters! @@ -312,14 +315,14 @@ private[channel] object ChannelCodecs3 { ("claimMainDelayedOutputTx" | optional(bool8, claimLocalDelayedOutputTxCodec)) :: ("htlcTxs" | mapCodec(outPointCodec, optional(bool8, htlcTxCodec))) :: ("claimHtlcDelayedTx" | listOfN(uint16, htlcDelayedTxCodec)) :: - ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec)) :: + ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec).xmap(txs => filterUnusedRemoteAnchorOutputTx(txs), { txs: List[ClaimAnchorOutputTx] => txs })) :: ("spent" | spentMapCodec)).as[LocalCommitPublished] val remoteCommitPublishedCodec: Codec[RemoteCommitPublished] = ( ("commitTx" | txCodec) :: ("claimMainOutputTx" | optional(bool8, claimRemoteCommitMainOutputTxCodec)) :: ("claimHtlcTxs" | mapCodec(outPointCodec, optional(bool8, claimHtlcTxCodec))) :: - ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec)) :: + ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec).xmap(txs => filterUnusedRemoteAnchorOutputTx(txs), { txs: List[ClaimAnchorOutputTx] => txs })) :: ("spent" | spentMapCodec)).as[RemoteCommitPublished] val revokedCommitPublishedCodec: Codec[RevokedCommitPublished] = ( diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version4/ChannelCodecs4.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version4/ChannelCodecs4.scala index 2169e640b8..5c60ccc1d3 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version4/ChannelCodecs4.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version4/ChannelCodecs4.scala @@ -148,10 +148,11 @@ private[channel] object ChannelCodecs4 { val mainPenaltyTxCodec: Codec[MainPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[MainPenaltyTx] val htlcPenaltyTxCodec: Codec[HtlcPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[HtlcPenaltyTx] val claimHtlcDelayedOutputPenaltyTxCodec: Codec[ClaimHtlcDelayedOutputPenaltyTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[ClaimHtlcDelayedOutputPenaltyTx] - val claimLocalAnchorOutputTxCodec: Codec[ClaimLocalAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | confirmationTarget)).as[ClaimLocalAnchorOutputTx] - private val claimLocalAnchorOutputTxBlockHeightConfirmCodec: Codec[ClaimLocalAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | blockHeightConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimLocalAnchorOutputTx] - private val claimLocalAnchorOutputTxNoConfirmCodec: Codec[ClaimLocalAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | defaultConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimLocalAnchorOutputTx] - private val claimRemoteAnchorOutputTxCodec: Codec[ClaimRemoteAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec)).as[ClaimRemoteAnchorOutputTx] + val claimLocalAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | confirmationTarget)).as[ClaimAnchorOutputTx] + private val claimLocalAnchorOutputTxBlockHeightConfirmCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | blockHeightConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] + private val claimLocalAnchorOutputTxNoConfirmCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("confirmationTarget" | defaultConfirmationTarget).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] + // We previously created an unused transaction spending the remote anchor (after the 16-blocks delay). + private val unusedRemoteAnchorOutputTxCodec: Codec[ClaimAnchorOutputTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: provide(ConfirmationTarget.Absolute(BlockHeight(42))).upcast[ConfirmationTarget]).as[ClaimAnchorOutputTx] val closingTxCodec: Codec[ClosingTx] = (("inputInfo" | inputInfoCodec) :: ("tx" | txCodec) :: ("outputIndex" | optional(bool8, outputInfoCodec))).as[ClosingTx] val claimRemoteCommitMainOutputTxCodec: Codec[ClaimRemoteCommitMainOutputTx] = discriminated[ClaimRemoteCommitMainOutputTx].by(uint8) @@ -163,7 +164,9 @@ private[channel] object ChannelCodecs4 { .typecase(0x12, claimLocalAnchorOutputTxCodec) .typecase(0x11, claimLocalAnchorOutputTxBlockHeightConfirmCodec) .typecase(0x01, claimLocalAnchorOutputTxNoConfirmCodec) - .typecase(0x02, claimRemoteAnchorOutputTxCodec) + .typecase(0x02, unusedRemoteAnchorOutputTxCodec) + + def filterUnusedRemoteAnchorOutputTx(anchorTxs: List[ClaimAnchorOutputTx]): List[ClaimAnchorOutputTx] = anchorTxs.filter(_.confirmationTarget != ConfirmationTarget.Absolute(BlockHeight(42))) val htlcTxCodec: Codec[HtlcTx] = discriminated[HtlcTx].by(uint8) // Important: order matters! @@ -596,14 +599,14 @@ private[channel] object ChannelCodecs4 { ("claimMainDelayedOutputTx" | optional(bool8, claimLocalDelayedOutputTxCodec)) :: ("htlcTxs" | mapCodec(outPointCodec, optional(bool8, htlcTxCodec))) :: ("claimHtlcDelayedTx" | listOfN(uint16, htlcDelayedTxCodec)) :: - ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec)) :: + ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec).xmap(txs => filterUnusedRemoteAnchorOutputTx(txs), { txs: List[ClaimAnchorOutputTx] => txs })) :: ("spent" | spentMapCodec)).as[LocalCommitPublished] val remoteCommitPublishedCodec: Codec[RemoteCommitPublished] = ( ("commitTx" | txCodec) :: ("claimMainOutputTx" | optional(bool8, claimRemoteCommitMainOutputTxCodec)) :: ("claimHtlcTxs" | mapCodec(outPointCodec, optional(bool8, claimHtlcTxCodec))) :: - ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec)) :: + ("claimAnchorTxs" | listOfN(uint16, claimAnchorOutputTxCodec).xmap(txs => filterUnusedRemoteAnchorOutputTx(txs), { txs: List[ClaimAnchorOutputTx] => txs })) :: ("spent" | spentMapCodec)).as[RemoteCommitPublished] val revokedCommitPublishedCodec: Codec[RevokedCommitPublished] = ( diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunderSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunderSpec.scala index 40d369925e..2c5b8b2e60 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunderSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxFunderSpec.scala @@ -37,7 +37,7 @@ import scala.util.Random class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike { - private def createAnchorTx(): (CommitTx, ClaimLocalAnchorOutputTx) = { + private def createAnchorTx(): (CommitTx, ClaimAnchorOutputTx) = { val anchorScript = Scripts.anchor(PlaceHolderPubKey) val commitInput = Funding.makeFundingInputInfo(randomTxId(), 1, 500 sat, PlaceHolderPubKey, PlaceHolderPubKey) val commitTx = Transaction( @@ -46,7 +46,7 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike { Seq(TxOut(330 sat, Script.pay2wsh(anchorScript))), 0 ) - val anchorTx = ClaimLocalAnchorOutputTx( + val anchorTx = ClaimAnchorOutputTx( InputInfo(OutPoint(commitTx, 0), commitTx.txOut.head, anchorScript), Transaction(2, Seq(TxIn(OutPoint(commitTx, 0), ByteVector.empty, 0)), Nil, 0), ConfirmationTarget.Absolute(BlockHeight(0)) @@ -136,7 +136,7 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike { test("adjust previous anchor transaction outputs") { val (commitTx, initialAnchorTx) = createAnchorTx() - val previousAnchorTx = ClaimLocalAnchorWithWitnessData(initialAnchorTx).updateTx(initialAnchorTx.tx.copy( + val previousAnchorTx = ClaimAnchorWithWitnessData(initialAnchorTx).updateTx(initialAnchorTx.tx.copy( txIn = Seq( initialAnchorTx.tx.txIn.head, // The previous funding attempt added two wallet inputs: diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala index 64fe89219a..b52e7bd7a7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala @@ -196,8 +196,8 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w // Forward the anchor tx to the publisher. val publishAnchor = alice2blockchain.expectMsgType[PublishReplaceableTx] assert(publishAnchor.txInfo.input.outPoint.txid == commitTx.tx.txid) - assert(publishAnchor.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) - val anchorTx = publishAnchor.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx].copy(confirmationTarget = ConfirmationTarget.Absolute(overrideCommitTarget)) + assert(publishAnchor.txInfo.isInstanceOf[ClaimAnchorOutputTx]) + val anchorTx = publishAnchor.txInfo.asInstanceOf[ClaimAnchorOutputTx].copy(confirmationTarget = ConfirmationTarget.Absolute(overrideCommitTarget)) (publishCommitTx, publishAnchor.copy(txInfo = anchorTx)) } @@ -213,8 +213,8 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w // Forward the anchor tx to the publisher. val publishAnchor = alice2blockchain.expectMsgType[PublishReplaceableTx] assert(publishAnchor.txInfo.input.outPoint.txid == commitTx.txid) - assert(publishAnchor.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) - val anchorTx = publishAnchor.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx].copy(confirmationTarget = ConfirmationTarget.Absolute(overrideCommitTarget)) + assert(publishAnchor.txInfo.isInstanceOf[ClaimAnchorOutputTx]) + val anchorTx = publishAnchor.txInfo.asInstanceOf[ClaimAnchorOutputTx].copy(confirmationTarget = ConfirmationTarget.Absolute(overrideCommitTarget)) (commitTx, publishAnchor.copy(txInfo = anchorTx)) } @@ -593,11 +593,11 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w val publishAnchor = alice2blockchain.expectMsgType[PublishReplaceableTx] assert(publishAnchor.commitTx == commitTx) assert(publishAnchor.txInfo.input.outPoint.txid == commitTx.txid) - assert(publishAnchor.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(publishAnchor.txInfo.isInstanceOf[ClaimAnchorOutputTx]) val targetFeerate = FeeratePerKw(3000 sat) setFeerate(targetFeerate) - val anchorTx = publishAnchor.copy(txInfo = publishAnchor.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx].copy(confirmationTarget = ConfirmationTarget.Absolute(aliceBlockHeight() + 6))) + val anchorTx = publishAnchor.copy(txInfo = publishAnchor.txInfo.asInstanceOf[ClaimAnchorOutputTx].copy(confirmationTarget = ConfirmationTarget.Absolute(aliceBlockHeight() + 6))) publisher ! Publish(probe.ref, anchorTx) // wait for the commit tx and anchor tx to be published val mempoolTxs = getMempoolTxs(2) @@ -939,7 +939,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w probe.send(alice, CMD_FORCECLOSE(probe.ref)) probe.expectMsgType[CommandSuccess[CMD_FORCECLOSE]] alice2blockchain.expectMsgType[PublishFinalTx] - assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) alice2blockchain.expectMsgType[PublishFinalTx] // claim main output val htlcSuccess = alice2blockchain.expectMsgType[PublishReplaceableTx] assert(htlcSuccess.txInfo.isInstanceOf[HtlcSuccessTx]) @@ -1011,7 +1011,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w probe.send(alice, CMD_FORCECLOSE(probe.ref)) probe.expectMsgType[CommandSuccess[CMD_FORCECLOSE]] alice2blockchain.expectMsgType[PublishFinalTx] - assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) alice2blockchain.expectMsgType[PublishFinalTx] // claim main output val htlcTimeout = alice2blockchain.expectMsgType[PublishReplaceableTx] assert(htlcTimeout.txInfo.isInstanceOf[HtlcTimeoutTx]) @@ -1058,7 +1058,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w probe.expectMsg(commitTx.tx.txid) generateBlocks(1) - assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) alice2blockchain.expectMsgType[PublishFinalTx] // claim main output val htlcSuccess = alice2blockchain.expectMsgType[PublishReplaceableTx] assert(htlcSuccess.txInfo.isInstanceOf[HtlcSuccessTx]) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/TxPublisherSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/TxPublisherSpec.scala index 1f0647aa4d..bba89374ca 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/TxPublisherSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/TxPublisherSpec.scala @@ -27,7 +27,7 @@ import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget} import fr.acinq.eclair.channel.publish import fr.acinq.eclair.channel.publish.TxPublisher.TxRejectedReason._ import fr.acinq.eclair.channel.publish.TxPublisher._ -import fr.acinq.eclair.transactions.Transactions.{ClaimLocalAnchorOutputTx, HtlcSuccessTx, InputInfo} +import fr.acinq.eclair.transactions.Transactions.{ClaimAnchorOutputTx, HtlcSuccessTx, InputInfo} import fr.acinq.eclair.{BlockHeight, NodeParams, TestConstants, TestKitBaseClass, randomBytes32, randomKey} import org.scalatest.Outcome import org.scalatest.funsuite.FixtureAnyFunSuiteLike @@ -105,7 +105,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { val confirmBefore = ConfirmationTarget.Absolute(nodeParams.currentBlockHeight + 12) val input = OutPoint(randomTxId(), 3) - val cmd = PublishReplaceableTx(ClaimLocalAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), confirmBefore), null, null) + val cmd = PublishReplaceableTx(ClaimAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), confirmBefore), null, null) txPublisher ! cmd val child = factory.expectMsgType[ReplaceableTxPublisherSpawned].actor val p = child.expectMsgType[ReplaceableTxPublisher.Publish] @@ -117,7 +117,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { val confirmBefore = nodeParams.currentBlockHeight + 12 val input = OutPoint(randomTxId(), 3) - val anchorTx = ClaimLocalAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), ConfirmationTarget.Priority(ConfirmationPriority.Medium)) + val anchorTx = ClaimAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), ConfirmationTarget.Priority(ConfirmationPriority.Medium)) val cmd = PublishReplaceableTx(anchorTx, null, null) txPublisher ! cmd val child = factory.expectMsgType[ReplaceableTxPublisherSpawned].actor @@ -175,7 +175,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned].actor attempt2.expectMsgType[FinalTxPublisher.Publish] - val cmd3 = PublishReplaceableTx(ClaimLocalAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, TxOut(20_000 sat, Nil) :: Nil, 0), ConfirmationTarget.Absolute(nodeParams.currentBlockHeight)), null, null) + val cmd3 = PublishReplaceableTx(ClaimAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, TxOut(20_000 sat, Nil) :: Nil, 0), ConfirmationTarget.Absolute(nodeParams.currentBlockHeight)), null, null) txPublisher ! cmd3 val attempt3 = factory.expectMsgType[ReplaceableTxPublisherSpawned].actor attempt3.expectMsgType[ReplaceableTxPublisher.Publish] @@ -197,7 +197,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned] attempt1.actor.expectMsgType[FinalTxPublisher.Publish] - val cmd2 = PublishReplaceableTx(ClaimLocalAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, TxOut(20_000 sat, Nil) :: Nil, 0), ConfirmationTarget.Absolute(nodeParams.currentBlockHeight)), null, null) + val cmd2 = PublishReplaceableTx(ClaimAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, TxOut(20_000 sat, Nil) :: Nil, 0), ConfirmationTarget.Absolute(nodeParams.currentBlockHeight)), null, null) txPublisher ! cmd2 val attempt2 = factory.expectMsgType[ReplaceableTxPublisherSpawned] attempt2.actor.expectMsgType[ReplaceableTxPublisher.Publish] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala index 063ff34a1b..4962ed2ccf 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala @@ -585,7 +585,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually { val commitInput = closingState.commitments.latest.commitInput Transaction.correctlySpends(publishedLocalCommitTx, Map(commitInput.outPoint -> commitInput.txOut), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) if (closingState.commitments.params.commitmentFormat.isInstanceOf[Transactions.AnchorOutputsCommitmentFormat]) { - assert(s2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(s2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) } // if s has a main output in the commit tx (when it has a non-dust balance), it should be claimed localCommitPublished.claimMainDelayedOutputTx.foreach(tx => s2blockchain.expectMsg(TxPublisher.PublishFinalTx(tx, tx.fee, None))) @@ -611,7 +611,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually { }) // we watch outputs of the commitment tx that both parties may spend and anchor outputs - val watchedOutputIndexes = localCommitPublished.htlcTxs.keySet.map(_.index) ++ localCommitPublished.claimAnchorTxs.collect { case tx: ClaimLocalAnchorOutputTx => tx.input.outPoint.index } + val watchedOutputIndexes = localCommitPublished.htlcTxs.keySet.map(_.index) ++ localCommitPublished.claimAnchorTxs.collect { case tx: ClaimAnchorOutputTx => tx.input.outPoint.index } val spentWatches = watchedOutputIndexes.map(_ => s2blockchain.expectMsgType[WatchOutputSpent]) spentWatches.foreach(ws => assert(ws.txId == commitTx.txid)) assert(spentWatches.map(_.outputIndex) == watchedOutputIndexes) @@ -635,7 +635,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually { closingData.commitments.params.commitmentFormat match { case _: AnchorOutputsCommitmentFormat => val anchorTx = s2blockchain.expectMsgType[PublishReplaceableTx] - assert(anchorTx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(anchorTx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) case Transactions.DefaultCommitmentFormat => () } // if s has a main output in the commit tx (when it has a non-dust balance), it should be claimed diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala index bc671235d3..2468c1f6c2 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala @@ -33,7 +33,7 @@ import fr.acinq.eclair.channel.publish.TxPublisher.{PublishFinalTx, SetChannelId import fr.acinq.eclair.channel.states.ChannelStateTestsBase.FakeTxPublisherFactory import fr.acinq.eclair.channel.states.{ChannelStateTestsBase, ChannelStateTestsTags} import fr.acinq.eclair.transactions.Transactions -import fr.acinq.eclair.transactions.Transactions.ClaimLocalAnchorOutputTx +import fr.acinq.eclair.transactions.Transactions.ClaimAnchorOutputTx import fr.acinq.eclair.wire.protocol._ import fr.acinq.eclair.{BlockHeight, MilliSatoshiLong, TestConstants, TestKitBaseClass, ToMilliSatoshiConversion} import org.scalatest.funsuite.FixtureAnyFunSuiteLike @@ -751,7 +751,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture val bobCommitTx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx alice ! WatchFundingSpentTriggered(bobCommitTx.tx) aliceListener.expectMsgType[TransactionPublished] - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMain = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMain.input.txid == bobCommitTx.tx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobCommitTx.tx.txid) @@ -787,7 +787,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture // Bob broadcasts his commit tx. alice ! WatchFundingSpentTriggered(bobCommitTx1) assert(aliceListener.expectMsgType[TransactionPublished].tx.txid == bobCommitTx1.txid) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMain = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMain.input.txid == bobCommitTx1.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobCommitTx1.txid) @@ -808,7 +808,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture assert(aliceListener.expectMsgType[TransactionConfirmed].tx == fundingTx) assert(alice2blockchain.expectMsgType[WatchFundingSpent].txId == fundingTx.txid) alice2 ! WatchFundingSpentTriggered(bobData.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainAlice = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainAlice.input.txid == bobData.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobData.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx.txid) @@ -819,7 +819,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture assert(bobListener.expectMsgType[TransactionConfirmed].tx == fundingTx) assert(bob2blockchain.expectMsgType[WatchFundingSpent].txId == fundingTx.txid) bob2 ! WatchFundingSpentTriggered(aliceData.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx) - assert(bob2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(bob2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainBob = bob2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainBob.input.txid == aliceData.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx.txid) assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId == aliceData.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx.txid) @@ -847,7 +847,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture assert(alice2blockchain.expectMsgType[WatchFundingSpent].txId == fundingTx1.txid) alice2blockchain.expectMsg(UnwatchTxConfirmed(fundingTx2.txId)) alice2 ! WatchFundingSpentTriggered(bobCommitTx1) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainAlice = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainAlice.input.txid == bobCommitTx1.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobCommitTx1.txid) @@ -860,7 +860,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture assert(bob2blockchain.expectMsgType[WatchFundingSpent].txId == fundingTx1.txid) bob2blockchain.expectMsg(UnwatchTxConfirmed(fundingTx2.txId)) bob2 ! WatchFundingSpentTriggered(aliceCommitTx1) - assert(bob2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(bob2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainBob = bob2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainBob.input.txid == aliceCommitTx1.txid) assert(bob2blockchain.expectMsgType[WatchTxConfirmed].txId == aliceCommitTx1.txid) @@ -1111,7 +1111,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture awaitCond(alice.stateName == CLOSING) aliceListener.expectMsgType[ChannelAborted] assert(alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx].tx.txid == aliceCommitTx.txid) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainLocal = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainLocal.input.txid == aliceCommitTx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == aliceCommitTx.txid) @@ -1120,7 +1120,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture val bobCommitTx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx alice ! WatchFundingSpentTriggered(bobCommitTx) alice2blockchain.expectMsgType[WatchOutputSpent] - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainRemote = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainRemote.input.txid == bobCommitTx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobCommitTx.txid) @@ -1145,7 +1145,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture awaitCond(alice.stateName == CLOSING) aliceListener.expectMsgType[ChannelAborted] assert(alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx].tx.txid == aliceCommitTx2.tx.txid) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMain2 = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMain2.input.txid == aliceCommitTx2.tx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == aliceCommitTx2.tx.txid) @@ -1158,7 +1158,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture assert(alice2blockchain.expectMsgType[WatchFundingSpent].txId == fundingTx1.txid) alice2blockchain.expectMsg(UnwatchTxConfirmed(fundingTx2.txId)) assert(alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx].tx.txid == aliceCommitTx1.tx.txid) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMain1 = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMain1.input.txid == aliceCommitTx1.tx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == aliceCommitTx1.tx.txid) @@ -1168,7 +1168,7 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture // Bob publishes his commit tx, Alice reacts by spending her remote main output. alice ! WatchFundingSpentTriggered(bobCommitTx1.tx) alice2blockchain.expectMsgType[WatchOutputSpent] - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMainRemote = alice2blockchain.expectMsgType[TxPublisher.PublishFinalTx] assert(claimMainRemote.input.txid == bobCommitTx1.tx.txid) assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobCommitTx1.tx.txid) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingReadyStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingReadyStateSpec.scala index e416cbaf20..19d2fdf159 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingReadyStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingReadyStateSpec.scala @@ -27,7 +27,7 @@ import fr.acinq.eclair.channel.publish.TxPublisher import fr.acinq.eclair.channel.publish.TxPublisher.SetChannelId import fr.acinq.eclair.channel.states.{ChannelStateTestsBase, ChannelStateTestsTags} import fr.acinq.eclair.payment.relay.Relayer.RelayFees -import fr.acinq.eclair.transactions.Transactions.ClaimLocalAnchorOutputTx +import fr.acinq.eclair.transactions.Transactions.ClaimAnchorOutputTx import fr.acinq.eclair.wire.protocol._ import fr.acinq.eclair.{BlockHeight, MilliSatoshiLong, TestConstants, TestKitBaseClass} import org.scalatest.OptionValues.convertOptionToValuable @@ -270,7 +270,7 @@ class WaitForDualFundingReadyStateSpec extends TestKitBaseClass with FixtureAnyF // bob publishes his commitment tx val bobCommitTx = bob.stateData.asInstanceOf[DATA_WAIT_FOR_DUAL_FUNDING_READY].commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx alice ! WatchFundingSpentTriggered(bobCommitTx) - assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[TxPublisher.PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) alice2blockchain.expectMsgType[TxPublisher.PublishTx] assert(alice2blockchain.expectMsgType[WatchTxConfirmed].txId == bobCommitTx.txid) listener.expectMsgType[ChannelAborted] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala index 2329b46a8e..1330310fdb 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala @@ -2987,7 +2987,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik alice ! WatchAlternativeCommitTxConfirmedTriggered(BlockHeight(400000), 42, bobCommitTx1) // we're back to the normal handling of remote commit inside(alice2blockchain.expectMsgType[PublishReplaceableTx]) { tx => - assert(tx.txInfo.isInstanceOf[Transactions.ClaimLocalAnchorOutputTx]) + assert(tx.txInfo.isInstanceOf[Transactions.ClaimAnchorOutputTx]) assert(tx.commitTx == bobCommitTx1) } val claimMain = alice2blockchain.expectMsgType[PublishFinalTx].tx diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala index 4ac576cab6..f059935ea7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala @@ -31,7 +31,7 @@ import fr.acinq.eclair.channel.states.{ChannelStateTestsBase, ChannelStateTestsT import fr.acinq.eclair.payment._ import fr.acinq.eclair.payment.relay.Relayer._ import fr.acinq.eclair.payment.send.SpontaneousRecipient -import fr.acinq.eclair.transactions.Transactions.ClaimLocalAnchorOutputTx +import fr.acinq.eclair.transactions.Transactions.ClaimAnchorOutputTx import fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, ChannelUpdate, ClosingSigned, CommitSig, Error, FailureMessageCodecs, FailureReason, PermanentChannelFailure, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFee, UpdateFulfillHtlc} import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, MilliSatoshiLong, TestConstants, TestKitBaseClass, randomBytes32, randomKey} import org.scalatest.funsuite.FixtureAnyFunSuiteLike @@ -725,7 +725,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit // in response to that, alice publishes her claim txs val anchorTx = alice2blockchain.expectMsgType[PublishReplaceableTx] - assert(anchorTx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(anchorTx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) val claimMain = alice2blockchain.expectMsgType[PublishFinalTx].tx // in addition to her main output, alice can only claim 2 out of 3 htlcs, she can't do anything regarding the htlc sent by bob for which she does not have the preimage val claimHtlcTxs = (1 to 2).map(_ => alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.tx) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala index 8cb3b22709..587b16d712 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala @@ -401,7 +401,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // Bob claims the htlc output from Alice's commit tx using its preimage. bob ! WatchFundingSpentTriggered(lcp.commitTx) if (initialState.commitments.params.channelFeatures.hasFeature(Features.AnchorOutputsZeroFeeHtlcTx)) { - assert(bob2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(bob2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) bob2blockchain.expectMsgType[PublishFinalTx] // main-delayed } val claimHtlcSuccessTx1 = bob2blockchain.expectMsgType[PublishReplaceableTx] @@ -514,7 +514,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // Alice prepares Claim-HTLC-timeout transactions for each HTLC. alice ! WatchFundingSpentTriggered(rcp.commitTx) if (alice.stateData.asInstanceOf[DATA_CLOSING].commitments.params.channelFeatures.hasFeature(Features.AnchorOutputsZeroFeeHtlcTx)) { - assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(alice2blockchain.expectMsgType[PublishFinalTx].desc == "remote-main-delayed") } Seq(htlc1, htlc2, htlc3).foreach(_ => assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimHtlcTimeoutTx])) @@ -604,7 +604,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with // Alice prepares Claim-HTLC-timeout transactions for each HTLC. alice ! WatchFundingSpentTriggered(rcp.commitTx) if (alice.stateData.asInstanceOf[DATA_CLOSING].commitments.params.channelFeatures.hasFeature(Features.AnchorOutputsZeroFeeHtlcTx)) { - assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(alice2blockchain.expectMsgType[PublishFinalTx].desc == "remote-main-delayed") } Seq(htlc1, htlc2, htlc3).foreach(_ => assert(alice2blockchain.expectMsgType[PublishReplaceableTx].txInfo.isInstanceOf[ClaimHtlcTimeoutTx])) @@ -664,16 +664,16 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(initialState.localCommitPublished.nonEmpty) val localCommitPublished1 = initialState.localCommitPublished.get assert(localCommitPublished1.claimAnchorTxs.nonEmpty) - val Some(localAnchor1) = localCommitPublished1.claimAnchorTxs.collectFirst { case tx: ClaimLocalAnchorOutputTx => tx } + val Some(localAnchor1) = localCommitPublished1.claimAnchorTxs.collectFirst { case tx: ClaimAnchorOutputTx => tx } assert(localAnchor1.confirmationTarget == ConfirmationTarget.Priority(ConfirmationPriority.Medium)) val replyTo = TestProbe() alice ! CMD_BUMP_FORCE_CLOSE_FEE(replyTo.ref, ConfirmationTarget.Priority(ConfirmationPriority.Fast)) replyTo.expectMsgType[RES_SUCCESS[CMD_BUMP_FORCE_CLOSE_FEE]] val localAnchor2 = inside(alice2blockchain.expectMsgType[PublishReplaceableTx]) { tx => - assert(tx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(tx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(tx.commitTx == localCommitPublished1.commitTx) - tx.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx] + tx.txInfo.asInstanceOf[ClaimAnchorOutputTx] } assert(localAnchor2.confirmationTarget == ConfirmationTarget.Priority(ConfirmationPriority.Fast)) val localCommitPublished2 = alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get @@ -683,9 +683,9 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice ! CMD_BUMP_FORCE_CLOSE_FEE(replyTo.ref, ConfirmationTarget.Priority(ConfirmationPriority.Medium)) replyTo.expectMsgType[RES_SUCCESS[CMD_BUMP_FORCE_CLOSE_FEE]] val localAnchor3 = inside(alice2blockchain.expectMsgType[PublishReplaceableTx]) { tx => - assert(tx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(tx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(tx.commitTx == localCommitPublished1.commitTx) - tx.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx] + tx.txInfo.asInstanceOf[ClaimAnchorOutputTx] } assert(localAnchor3.confirmationTarget == ConfirmationTarget.Priority(ConfirmationPriority.Fast)) assert(alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.contains(localCommitPublished2)) @@ -1189,16 +1189,16 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with val bobCommitTx = bobCommitTxs.last.commitTx.tx val closingState1 = remoteClose(bobCommitTx, alice, alice2blockchain) assert(closingState1.claimAnchorTxs.nonEmpty) - val Some(localAnchor1) = closingState1.claimAnchorTxs.collectFirst { case tx: ClaimLocalAnchorOutputTx => tx } + val Some(localAnchor1) = closingState1.claimAnchorTxs.collectFirst { case tx: ClaimAnchorOutputTx => tx } assert(localAnchor1.confirmationTarget == ConfirmationTarget.Priority(ConfirmationPriority.Medium)) val replyTo = TestProbe() alice ! CMD_BUMP_FORCE_CLOSE_FEE(replyTo.ref, ConfirmationTarget.Priority(ConfirmationPriority.Fast)) replyTo.expectMsgType[RES_SUCCESS[CMD_BUMP_FORCE_CLOSE_FEE]] val localAnchor2 = inside(alice2blockchain.expectMsgType[PublishReplaceableTx]) { tx => - assert(tx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(tx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(tx.commitTx == bobCommitTx) - tx.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx] + tx.txInfo.asInstanceOf[ClaimAnchorOutputTx] } assert(localAnchor2.confirmationTarget == ConfirmationTarget.Priority(ConfirmationPriority.Fast)) val closingState2 = alice.stateData.asInstanceOf[DATA_CLOSING].remoteCommitPublished.get @@ -1208,9 +1208,9 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice ! CMD_BUMP_FORCE_CLOSE_FEE(replyTo.ref, ConfirmationTarget.Priority(ConfirmationPriority.Medium)) replyTo.expectMsgType[RES_SUCCESS[CMD_BUMP_FORCE_CLOSE_FEE]] val localAnchor3 = inside(alice2blockchain.expectMsgType[PublishReplaceableTx]) { tx => - assert(tx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(tx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(tx.commitTx == bobCommitTx) - tx.txInfo.asInstanceOf[ClaimLocalAnchorOutputTx] + tx.txInfo.asInstanceOf[ClaimAnchorOutputTx] } assert(localAnchor3.confirmationTarget == ConfirmationTarget.Priority(ConfirmationPriority.Fast)) assert(alice.stateData.asInstanceOf[DATA_CLOSING].remoteCommitPublished.contains(closingState2)) @@ -1570,7 +1570,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice2blockchain.expectMsgType[WatchFundingSpent] // then we should re-publish unconfirmed transactions inside(alice2blockchain.expectMsgType[PublishReplaceableTx]) { tx => - assert(tx.txInfo.isInstanceOf[ClaimLocalAnchorOutputTx]) + assert(tx.txInfo.isInstanceOf[ClaimAnchorOutputTx]) assert(tx.commitTx == bobCommitTx) } closingState.claimMainOutputTx.foreach(claimMain => assert(alice2blockchain.expectMsgType[PublishFinalTx].tx == claimMain.tx)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala index b6ce30a205..79714450f6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala @@ -43,19 +43,17 @@ import scala.util.{Random, Try} */ class TransactionsSpec extends AnyFunSuite with Logging { - val localFundingPriv = PrivateKey(randomBytes32()) - val remoteFundingPriv = PrivateKey(randomBytes32()) - val localRevocationPriv = PrivateKey(randomBytes32()) - val localPaymentPriv = PrivateKey(randomBytes32()) - val localDelayedPaymentPriv = PrivateKey(randomBytes32()) - val remotePaymentPriv = PrivateKey(randomBytes32()) - val localHtlcPriv = PrivateKey(randomBytes32()) - val remoteHtlcPriv = PrivateKey(randomBytes32()) - val finalPubKeyScript = Script.write(Script.pay2wpkh(PrivateKey(randomBytes32()).publicKey)) - val commitInput = Funding.makeFundingInputInfo(randomTxId(), 0, Btc(1), localFundingPriv.publicKey, remoteFundingPriv.publicKey) - val toLocalDelay = CltvExpiryDelta(144) - val localDustLimit = Satoshi(546) - val feeratePerKw = FeeratePerKw(22000 sat) + private val localFundingPriv = randomKey() + private val remoteFundingPriv = randomKey() + private val localRevocationPriv = randomKey() + private val localPaymentPriv = randomKey() + private val localDelayedPaymentPriv = randomKey() + private val remotePaymentPriv = randomKey() + private val localHtlcPriv = randomKey() + private val remoteHtlcPriv = randomKey() + private val toLocalDelay = CltvExpiryDelta(144) + private val localDustLimit = Satoshi(546) + private val feeratePerKw = FeeratePerKw(22000 sat) test("extract csv and cltv timeouts") { val parentTxId1 = randomTxId() @@ -200,7 +198,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { // first we create a fake commitTx tx, containing only the output that will be spent by the ClaimAnchorOutputTx val pubKeyScript = write(pay2wsh(anchor(localFundingPriv.publicKey))) val commitTx = Transaction(version = 2, txIn = Nil, txOut = TxOut(anchorAmount, pubKeyScript) :: Nil, lockTime = 0) - val Right(claimAnchorOutputTx) = makeClaimLocalAnchorOutputTx(commitTx, localFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(1105))) + val Right(claimAnchorOutputTx) = makeClaimAnchorOutputTx(commitTx, localFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(1105))) assert(claimAnchorOutputTx.tx.txOut.isEmpty) assert(claimAnchorOutputTx.confirmationTarget == ConfirmationTarget.Absolute(BlockHeight(1105))) // we will always add at least one input and one output to be able to set our desired feerate @@ -589,7 +587,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { OutPoint(randomTxId(), 3) -> TxOut(walletAmount, Script.pay2wpkh(walletPub)), OutPoint(randomTxId(), 0) -> TxOut(walletAmount, Script.pay2wpkh(walletPub)), ) - val Right(claimAnchorOutputTx) = makeClaimLocalAnchorOutputTx(commitTx.tx, localFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(0))).map(anchorTx => { + val Right(claimAnchorOutputTx) = makeClaimAnchorOutputTx(commitTx.tx, localFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(0))).map(anchorTx => { val walletTxIn = walletInputs.map { case (outpoint, _) => TxIn(outpoint, ByteVector.empty, 0) } val unsignedTx = anchorTx.tx.copy(txIn = anchorTx.tx.txIn ++ walletTxIn) val sig1 = unsignedTx.signInput(1, Script.pay2pkh(walletPub), SIGHASH_ALL, walletAmount, SigVersion.SIGVERSION_WITNESS_V0, walletPriv) @@ -610,7 +608,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { } { // remote spends remote anchor - val Right(claimAnchorOutputTx) = makeClaimLocalAnchorOutputTx(commitTx.tx, remoteFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(0))) + val Right(claimAnchorOutputTx) = makeClaimAnchorOutputTx(commitTx.tx, remoteFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(0))) assert(checkSpendable(claimAnchorOutputTx).isFailure) val localSig = claimAnchorOutputTx.sign(remoteFundingPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat, Map.empty) val signedTx = addSigs(claimAnchorOutputTx, localSig) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3Spec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3Spec.scala index f85299f4e2..4a6fd9a021 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3Spec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/version3/ChannelCodecs3Spec.scala @@ -20,7 +20,7 @@ import fr.acinq.eclair.channel._ import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.wire.internal.channel.version3.ChannelCodecs3.Codecs._ import fr.acinq.eclair.wire.internal.channel.version3.ChannelCodecs3.channelDataCodec -import fr.acinq.eclair.{BlockHeight, RealShortChannelId, ShortChannelId} +import fr.acinq.eclair.{BlockHeight, ShortChannelId} import org.scalatest.funsuite.AnyFunSuite import scodec.bits.HexStringSyntax @@ -81,8 +81,8 @@ class ChannelCodecs3Spec extends AnyFunSuite { { val oldAnchorTxBin = hex"0011 24bd0be30e31c748c7afdde7d2c527d711fadf88500971a4a1136bca375dba07b8000000002b4a0100000000000022002036c067df8952dbcd5db347e7c152ca3fa4514f2072d27867837b1c2d319a7e01282103cc89f1459b5201cda08e08c6fb7b1968c54e8172c555896da27c6fdc10522ceeac736460b268330200000001bd0be30e31c748c7afdde7d2c527d711fadf88500971a4a1136bca375dba07b80000000000000000000000000000" val oldAnchorTx = txWithInputInfoCodec.decode(oldAnchorTxBin.bits).require.value - assert(oldAnchorTx.isInstanceOf[ClaimLocalAnchorOutputTx]) - assert(oldAnchorTx.asInstanceOf[ClaimLocalAnchorOutputTx].confirmationTarget == ConfirmationTarget.Absolute(BlockHeight(0))) + assert(oldAnchorTx.isInstanceOf[ClaimAnchorOutputTx]) + assert(oldAnchorTx.asInstanceOf[ClaimAnchorOutputTx].confirmationTarget == ConfirmationTarget.Absolute(BlockHeight(0))) } { val oldHtlcSuccessTxBin = hex"0002 24f5580de0577271dce09d2de26e19ec58bf2373b0171473291a8f8be9b04fb289000000002bb0ad010000000000220020462cf8912ffc5f27764c109bed188950500011a2837ff8b9c8f9a39cffa395a58b76a91406b0950d9feded82239b3e6c9082308900f389de8763ac672102d65aa07658a7214ff129f91a1a22ade2ea4d1b07cc14b2f85a2842c34240836f7c8201208763a914c461c897e2165c7e44e14850dfcfd68f99127aed88527c21033c8d41cfbe1511a909b63fed68e75a29c3ce30418c39bbb8294c8b36c6a6c16a52ae677503101b06b175ac6868fd01a002000000000101f5580de0577271dce09d2de26e19ec58bf2373b0171473291a8f8be9b04fb289000000000000000000013a920100000000002200208742b16c9fd4e74854dcd84322dd1de06f7993fe627fd2ca0be4b996a936d56b050047304402201b4527c8f420852550af00bbd9149db9b31adcb7e1f127766e75e1e01746df0302202a57bb1e274ed7d3e8dbe5f205de721a23092c1e2ce2135f4750f18f6c0b51b001483045022100b6df309c8e5746a077b1f7c2f299528e164946bd514a5049475af7f5665805da0220392ae877112a3c52f74d190b354b4f5c020da9c1a71a7a08ced0a5363e795a27012017ea8f5afde8f708258d5669e1bbd454e82ddca8c6c480ec5302b4b1e8051d3d8b76a91406b0950d9feded82239b3e6c9082308900f389de8763ac672102d65aa07658a7214ff129f91a1a22ade2ea4d1b07cc14b2f85a2842c34240836f7c8201208763a914c461c897e2165c7e44e14850dfcfd68f99127aed88527c21033c8d41cfbe1511a909b63fed68e75a29c3ce30418c39bbb8294c8b36c6a6c16a52ae677503101b06b175ac686800000000dc7002a387673f17ebaf08545ccec712a9b6914813cdb83b4270932294f20f660000000000000000"