diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala index 0ad044adc0..7bc2b423b9 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -225,7 +225,7 @@ object LocalCommit { fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo, commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey): Either[ChannelException, LocalCommit] = { val (localCommitTx, htlcTxs) = Commitment.makeLocalTxs(keyManager, params.channelConfig, params.channelFeatures, localCommitIndex, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, localPerCommitmentPoint, spec) - if (!checkSig(localCommitTx, commit.signature, remoteFundingPubKey, TxOwner.Remote, params.commitmentFormat)) { + if (!localCommitTx.checkSig(commit.signature, remoteFundingPubKey, TxOwner.Remote, params.commitmentFormat)) { return Left(InvalidCommitmentSignature(params.channelId, fundingTxId, fundingTxIndex, localCommitTx.tx)) } val sortedHtlcTxs = htlcTxs.sortBy(_.input.outPoint.index) @@ -235,7 +235,7 @@ object LocalCommit { val remoteHtlcPubkey = Generators.derivePubKey(params.remoteParams.htlcBasepoint, localPerCommitmentPoint) val htlcTxsAndRemoteSigs = sortedHtlcTxs.zip(commit.htlcSignatures).toList.map { case (htlcTx: HtlcTx, remoteSig) => - if (!checkSig(htlcTx, remoteSig, remoteHtlcPubkey, TxOwner.Remote, params.commitmentFormat)) { + if (!htlcTx.checkSig(remoteSig, remoteHtlcPubkey, TxOwner.Remote, params.commitmentFormat)) { return Left(InvalidHtlcSignature(params.channelId, htlcTx.tx.txid)) } HtlcTxAndRemoteSig(htlcTx, remoteSig) @@ -1142,7 +1142,10 @@ case class Commitments(params: ChannelParams, val localFundingKey = keyManager.fundingPublicKey(params.localParams.fundingKeyPath, commitment.fundingTxIndex).publicKey val remoteFundingKey = commitment.remoteFundingPubKey val fundingScript = Script.write(Scripts.multiSig2of2(localFundingKey, remoteFundingKey)) - commitment.commitInput.redeemScript == fundingScript + commitment.commitInput match { + case InputInfo.SegwitInput(_, _, redeemScript) => redeemScript == fundingScript + case _: InputInfo.TaprootInput => false + } } } 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 78db6d35d0..608733ac91 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 @@ -376,10 +376,10 @@ object Helpers { def makeFundingPubKeyScript(localFundingKey: PublicKey, remoteFundingKey: PublicKey): ByteVector = write(pay2wsh(multiSig2of2(localFundingKey, remoteFundingKey))) - def makeFundingInputInfo(fundingTxId: TxId, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: PublicKey, fundingPubkey2: PublicKey): InputInfo = { + def makeFundingInputInfo(fundingTxId: TxId, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: PublicKey, fundingPubkey2: PublicKey): InputInfo.SegwitInput = { val fundingScript = multiSig2of2(fundingPubkey1, fundingPubkey2) val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript)) - InputInfo(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript)) + InputInfo.SegwitInput(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript)) } /** 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 d0be968257..02bfb485dd 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 @@ -358,12 +358,16 @@ private class ReplaceableTxFunder(nodeParams: NodeParams, import fr.acinq.bitcoin.scalacompat.KotlinUtils._ // We create a PSBT with the non-wallet input already signed: + val witnessScript = locallySignedTx.txInfo.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => fr.acinq.bitcoin.Script.parse(redeemScript) + case _: InputInfo.TaprootInput => null + } val psbt = new Psbt(locallySignedTx.txInfo.tx) .updateWitnessInput( locallySignedTx.txInfo.input.outPoint, locallySignedTx.txInfo.input.txOut, null, - fr.acinq.bitcoin.Script.parse(locallySignedTx.txInfo.input.redeemScript), + witnessScript, fr.acinq.bitcoin.SigHash.SIGHASH_ALL, java.util.Map.of(), null, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/keymanager/LocalChannelKeyManager.scala b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/keymanager/LocalChannelKeyManager.scala index 75ab2736ac..86975a1ae8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/keymanager/LocalChannelKeyManager.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/keymanager/LocalChannelKeyManager.scala @@ -23,7 +23,6 @@ import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32, ByteVector6 import fr.acinq.eclair.crypto.Generators import fr.acinq.eclair.crypto.Monitoring.{Metrics, Tags} import fr.acinq.eclair.router.Announcements -import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, TransactionWithInputInfo, TxOwner} import fr.acinq.eclair.{KamonExt, randomLong} import grizzled.slf4j.Logging @@ -113,7 +112,7 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: BlockHash) extends Cha Metrics.SignTxCount.withTags(tags).increment() KamonExt.time(Metrics.SignTxDuration.withTags(tags)) { val privateKey = privateKeys.get(publicKey.path) - Transactions.sign(tx, privateKey.privateKey, txOwner, commitmentFormat) + tx.sign(privateKey.privateKey, txOwner, commitmentFormat) } } @@ -134,7 +133,7 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: BlockHash) extends Cha KamonExt.time(Metrics.SignTxDuration.withTags(tags)) { val privateKey = privateKeys.get(publicKey.path) val currentKey = Generators.derivePrivKey(privateKey.privateKey, remotePoint) - Transactions.sign(tx, currentKey, txOwner, commitmentFormat) + tx.sign(currentKey, txOwner, commitmentFormat) } } @@ -154,7 +153,7 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: BlockHash) extends Cha KamonExt.time(Metrics.SignTxDuration.withTags(tags)) { val privateKey = privateKeys.get(publicKey.path) val currentKey = Generators.revocationPrivKey(privateKey.privateKey, remoteSecret) - Transactions.sign(tx, currentKey, txOwner, commitmentFormat) + tx.sign(currentKey, txOwner, commitmentFormat) } } 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 286a93dafe..79530181f9 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 @@ -16,12 +16,12 @@ package fr.acinq.eclair.transactions -import fr.acinq.bitcoin.ScriptFlags import fr.acinq.bitcoin.SigHash._ import fr.acinq.bitcoin.SigVersion._ -import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, ripemd160} +import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, XonlyPublicKey, ripemd160} import fr.acinq.bitcoin.scalacompat.Script._ import fr.acinq.bitcoin.scalacompat._ +import fr.acinq.bitcoin.{ScriptFlags, ScriptTree} import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw} import fr.acinq.eclair.transactions.CommitmentOutput._ @@ -94,9 +94,21 @@ object Transactions { // @formatter:off case class OutputInfo(index: Long, amount: Satoshi, publicKeyScript: ByteVector) - case class InputInfo(outPoint: OutPoint, txOut: TxOut, redeemScript: ByteVector) + + sealed trait InputInfo { + val outPoint: OutPoint + val txOut: TxOut + } + object InputInfo { - def apply(outPoint: OutPoint, txOut: TxOut, redeemScript: Seq[ScriptElt]) = new InputInfo(outPoint, txOut, Script.write(redeemScript)) + case class SegwitInput(outPoint: OutPoint, txOut: TxOut, redeemScript: ByteVector) extends InputInfo + case class TaprootInput(outPoint: OutPoint, txOut: TxOut, internalKey: XonlyPublicKey, scriptTree_opt: Option[ScriptTree]) extends InputInfo { + val publicKeyScript: ByteVector = Script.write(Script.pay2tr(internalKey, scriptTree_opt)) + } + + def apply(outPoint: OutPoint, txOut: TxOut, redeemScript: ByteVector): SegwitInput = SegwitInput(outPoint, txOut, redeemScript) + def apply(outPoint: OutPoint, txOut: TxOut, redeemScript: Seq[ScriptElt]): SegwitInput = SegwitInput(outPoint, txOut, Script.write(redeemScript)) + def apply(outPoint: OutPoint, txOut: TxOut, internalKey: XonlyPublicKey, scriptTree_opt: Option[ScriptTree]): TaprootInput = TaprootInput(outPoint, txOut, internalKey, scriptTree_opt) } /** Owner of a given transaction (local/remote). */ @@ -118,7 +130,35 @@ object Transactions { } /** Sighash flags to use when signing the transaction. */ def sighash(txOwner: TxOwner, commitmentFormat: CommitmentFormat): Int = SIGHASH_ALL + + def sign(key: PrivateKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64 = { + sign(key, sighash(txOwner, commitmentFormat)) + } + + def sign(key: PrivateKey, sighashType: Int): ByteVector64 = input match { + case _: InputInfo.TaprootInput => ByteVector64.Zeroes + case InputInfo.SegwitInput(outPoint, txOut, redeemScript) => + // NB: the tx may have multiple inputs, we will only sign the one provided in txinfo.input. Bear in mind that the + // signature will be invalidated if other inputs are added *afterwards* and sighashType was SIGHASH_ALL. + val inputIndex = tx.txIn.indexWhere(_.outPoint == outPoint) + val sigDER = Transaction.signInput(tx, inputIndex, redeemScript, sighashType, txOut.amount, SIGVERSION_WITNESS_V0, key) + Crypto.der2compact(sigDER) + } + + def checkSig(sig: ByteVector64, pubKey: PublicKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): Boolean = input match { + case _: InputInfo.TaprootInput => false + case InputInfo.SegwitInput(outPoint, txOut, redeemScript) => + val sighash = this.sighash(txOwner, commitmentFormat) + val inputIndex = tx.txIn.indexWhere(_.outPoint == outPoint) + if (inputIndex >= 0) { + val data = Transaction.hashForSigning(tx, inputIndex, redeemScript, sighash, txOut.amount, SIGVERSION_WITNESS_V0) + Crypto.verifySignature(data, sig, pubKey) + } else { + false + } + } } + sealed trait ReplaceableTransactionWithInputInfo extends TransactionWithInputInfo { /** Block before which the transaction must be confirmed. */ def confirmationTarget: ConfirmationTarget @@ -846,54 +886,51 @@ object Transactions { val PlaceHolderSig = ByteVector64(ByteVector.fill(64)(0xaa)) assert(der(PlaceHolderSig).size == 72) - private def sign(tx: Transaction, redeemScript: ByteVector, amount: Satoshi, key: PrivateKey, sighashType: Int, inputIndex: Int): ByteVector64 = { - val sigDER = Transaction.signInput(tx, inputIndex, redeemScript, sighashType, amount, SIGVERSION_WITNESS_V0, key) - val sig64 = Crypto.der2compact(sigDER) - sig64 - } - - def sign(txinfo: TransactionWithInputInfo, key: PrivateKey, sighashType: Int): ByteVector64 = { - // NB: the tx may have multiple inputs, we will only sign the one provided in txinfo.input. Bear in mind that the - // signature will be invalidated if other inputs are added *afterwards* and sighashType was SIGHASH_ALL. - val inputIndex = txinfo.tx.txIn.zipWithIndex.find(_._1.outPoint == txinfo.input.outPoint).get._2 - sign(txinfo.tx, txinfo.input.redeemScript, txinfo.input.txOut.amount, key, sighashType, inputIndex) - } - - def sign(txinfo: TransactionWithInputInfo, key: PrivateKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64 = sign(txinfo, key, txinfo.sighash(txOwner, commitmentFormat)) - def addSigs(commitTx: CommitTx, localFundingPubkey: PublicKey, remoteFundingPubkey: PublicKey, localSig: ByteVector64, remoteSig: ByteVector64): CommitTx = { val witness = Scripts.witness2of2(localSig, remoteSig, localFundingPubkey, remoteFundingPubkey) commitTx.copy(tx = commitTx.tx.updateWitness(0, witness)) } - def addSigs(mainPenaltyTx: MainPenaltyTx, revocationSig: ByteVector64): MainPenaltyTx = { - val witness = Scripts.witnessToLocalDelayedWithRevocationSig(revocationSig, mainPenaltyTx.input.redeemScript) - mainPenaltyTx.copy(tx = mainPenaltyTx.tx.updateWitness(0, witness)) + def addSigs(mainPenaltyTx: MainPenaltyTx, revocationSig: ByteVector64): MainPenaltyTx = mainPenaltyTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = Scripts.witnessToLocalDelayedWithRevocationSig(revocationSig, redeemScript) + mainPenaltyTx.copy(tx = mainPenaltyTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => mainPenaltyTx } - def addSigs(htlcPenaltyTx: HtlcPenaltyTx, revocationSig: ByteVector64, revocationPubkey: PublicKey): HtlcPenaltyTx = { - val witness = Scripts.witnessHtlcWithRevocationSig(revocationSig, revocationPubkey, htlcPenaltyTx.input.redeemScript) - htlcPenaltyTx.copy(tx = htlcPenaltyTx.tx.updateWitness(0, witness)) + def addSigs(htlcPenaltyTx: HtlcPenaltyTx, revocationSig: ByteVector64, revocationPubkey: PublicKey): HtlcPenaltyTx = htlcPenaltyTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = Scripts.witnessHtlcWithRevocationSig(revocationSig, revocationPubkey, redeemScript) + htlcPenaltyTx.copy(tx = htlcPenaltyTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => htlcPenaltyTx } - def addSigs(htlcSuccessTx: HtlcSuccessTx, localSig: ByteVector64, remoteSig: ByteVector64, paymentPreimage: ByteVector32, commitmentFormat: CommitmentFormat): HtlcSuccessTx = { - val witness = witnessHtlcSuccess(localSig, remoteSig, paymentPreimage, htlcSuccessTx.input.redeemScript, commitmentFormat) - htlcSuccessTx.copy(tx = htlcSuccessTx.tx.updateWitness(0, witness)) + def addSigs(htlcSuccessTx: HtlcSuccessTx, localSig: ByteVector64, remoteSig: ByteVector64, paymentPreimage: ByteVector32, commitmentFormat: CommitmentFormat): HtlcSuccessTx = htlcSuccessTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessHtlcSuccess(localSig, remoteSig, paymentPreimage, redeemScript, commitmentFormat) + htlcSuccessTx.copy(tx = htlcSuccessTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => htlcSuccessTx } - def addSigs(htlcTimeoutTx: HtlcTimeoutTx, localSig: ByteVector64, remoteSig: ByteVector64, commitmentFormat: CommitmentFormat): HtlcTimeoutTx = { - val witness = witnessHtlcTimeout(localSig, remoteSig, htlcTimeoutTx.input.redeemScript, commitmentFormat) - htlcTimeoutTx.copy(tx = htlcTimeoutTx.tx.updateWitness(0, witness)) + def addSigs(htlcTimeoutTx: HtlcTimeoutTx, localSig: ByteVector64, remoteSig: ByteVector64, commitmentFormat: CommitmentFormat): HtlcTimeoutTx = htlcTimeoutTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessHtlcTimeout(localSig, remoteSig, redeemScript, commitmentFormat) + htlcTimeoutTx.copy(tx = htlcTimeoutTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => htlcTimeoutTx } - def addSigs(claimHtlcSuccessTx: ClaimHtlcSuccessTx, localSig: ByteVector64, paymentPreimage: ByteVector32): ClaimHtlcSuccessTx = { - val witness = witnessClaimHtlcSuccessFromCommitTx(localSig, paymentPreimage, claimHtlcSuccessTx.input.redeemScript) - claimHtlcSuccessTx.copy(tx = claimHtlcSuccessTx.tx.updateWitness(0, witness)) + def addSigs(claimHtlcSuccessTx: ClaimHtlcSuccessTx, localSig: ByteVector64, paymentPreimage: ByteVector32): ClaimHtlcSuccessTx = claimHtlcSuccessTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessClaimHtlcSuccessFromCommitTx(localSig, paymentPreimage, redeemScript) + claimHtlcSuccessTx.copy(tx = claimHtlcSuccessTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => claimHtlcSuccessTx } - def addSigs(claimHtlcTimeoutTx: ClaimHtlcTimeoutTx, localSig: ByteVector64): ClaimHtlcTimeoutTx = { - val witness = witnessClaimHtlcTimeoutFromCommitTx(localSig, claimHtlcTimeoutTx.input.redeemScript) - claimHtlcTimeoutTx.copy(tx = claimHtlcTimeoutTx.tx.updateWitness(0, witness)) + def addSigs(claimHtlcTimeoutTx: ClaimHtlcTimeoutTx, localSig: ByteVector64): ClaimHtlcTimeoutTx = claimHtlcTimeoutTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessClaimHtlcTimeoutFromCommitTx(localSig, redeemScript) + claimHtlcTimeoutTx.copy(tx = claimHtlcTimeoutTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => claimHtlcTimeoutTx } def addSigs(claimP2WPKHOutputTx: ClaimP2WPKHOutputTx, localPaymentPubkey: PublicKey, localSig: ByteVector64): ClaimP2WPKHOutputTx = { @@ -901,29 +938,39 @@ object Transactions { claimP2WPKHOutputTx.copy(tx = claimP2WPKHOutputTx.tx.updateWitness(0, witness)) } - def addSigs(claimRemoteDelayedOutputTx: ClaimRemoteDelayedOutputTx, localSig: ByteVector64): ClaimRemoteDelayedOutputTx = { - val witness = witnessClaimToRemoteDelayedFromCommitTx(localSig, claimRemoteDelayedOutputTx.input.redeemScript) - claimRemoteDelayedOutputTx.copy(tx = claimRemoteDelayedOutputTx.tx.updateWitness(0, witness)) + def addSigs(claimRemoteDelayedOutputTx: ClaimRemoteDelayedOutputTx, localSig: ByteVector64): ClaimRemoteDelayedOutputTx = claimRemoteDelayedOutputTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessClaimToRemoteDelayedFromCommitTx(localSig, redeemScript) + claimRemoteDelayedOutputTx.copy(tx = claimRemoteDelayedOutputTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => claimRemoteDelayedOutputTx } - def addSigs(claimDelayedOutputTx: ClaimLocalDelayedOutputTx, localSig: ByteVector64): ClaimLocalDelayedOutputTx = { - val witness = witnessToLocalDelayedAfterDelay(localSig, claimDelayedOutputTx.input.redeemScript) - claimDelayedOutputTx.copy(tx = claimDelayedOutputTx.tx.updateWitness(0, witness)) + def addSigs(claimDelayedOutputTx: ClaimLocalDelayedOutputTx, localSig: ByteVector64): ClaimLocalDelayedOutputTx = claimDelayedOutputTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessToLocalDelayedAfterDelay(localSig, redeemScript) + claimDelayedOutputTx.copy(tx = claimDelayedOutputTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => claimDelayedOutputTx } - def addSigs(htlcDelayedTx: HtlcDelayedTx, localSig: ByteVector64): HtlcDelayedTx = { - val witness = witnessToLocalDelayedAfterDelay(localSig, htlcDelayedTx.input.redeemScript) - htlcDelayedTx.copy(tx = htlcDelayedTx.tx.updateWitness(0, witness)) + def addSigs(htlcDelayedTx: HtlcDelayedTx, localSig: ByteVector64): HtlcDelayedTx = htlcDelayedTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessToLocalDelayedAfterDelay(localSig, redeemScript) + htlcDelayedTx.copy(tx = htlcDelayedTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => htlcDelayedTx } - def addSigs(claimAnchorOutputTx: ClaimLocalAnchorOutputTx, localSig: ByteVector64): ClaimLocalAnchorOutputTx = { - val witness = witnessAnchor(localSig, claimAnchorOutputTx.input.redeemScript) - claimAnchorOutputTx.copy(tx = claimAnchorOutputTx.tx.updateWitness(0, witness)) + def addSigs(claimAnchorOutputTx: ClaimLocalAnchorOutputTx, localSig: ByteVector64): ClaimLocalAnchorOutputTx = claimAnchorOutputTx.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = witnessAnchor(localSig, redeemScript) + claimAnchorOutputTx.copy(tx = claimAnchorOutputTx.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => claimAnchorOutputTx } - def addSigs(claimHtlcDelayedPenalty: ClaimHtlcDelayedOutputPenaltyTx, revocationSig: ByteVector64): ClaimHtlcDelayedOutputPenaltyTx = { - val witness = Scripts.witnessToLocalDelayedWithRevocationSig(revocationSig, claimHtlcDelayedPenalty.input.redeemScript) - claimHtlcDelayedPenalty.copy(tx = claimHtlcDelayedPenalty.tx.updateWitness(0, witness)) + def addSigs(claimHtlcDelayedPenalty: ClaimHtlcDelayedOutputPenaltyTx, revocationSig: ByteVector64): ClaimHtlcDelayedOutputPenaltyTx = claimHtlcDelayedPenalty.input match { + case InputInfo.SegwitInput(_, _, redeemScript) => + val witness = Scripts.witnessToLocalDelayedWithRevocationSig(revocationSig, redeemScript) + claimHtlcDelayedPenalty.copy(tx = claimHtlcDelayedPenalty.tx.updateWitness(0, witness)) + case _: InputInfo.TaprootInput => claimHtlcDelayedPenalty } def addSigs(closingTx: ClosingTx, localFundingPubkey: PublicKey, remoteFundingPubkey: PublicKey, localSig: ByteVector64, remoteSig: ByteVector64): ClosingTx = { @@ -935,11 +982,4 @@ object Transactions { // NB: we don't verify the other inputs as they should only be wallet inputs used to RBF the transaction Try(Transaction.correctlySpends(txinfo.tx, Map(txinfo.input.outPoint -> txinfo.input.txOut), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) } - - def checkSig(txinfo: TransactionWithInputInfo, sig: ByteVector64, pubKey: PublicKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): Boolean = { - val sighash = txinfo.sighash(txOwner, commitmentFormat) - val data = Transaction.hashForSigning(txinfo.tx, inputIndex = 0, txinfo.input.redeemScript, sighash, txinfo.input.txOut.amount, SIGVERSION_WITNESS_V0) - Crypto.verifySignature(data, sig, pubKey) - } - } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version0/ChannelCodecs0.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version0/ChannelCodecs0.scala index e1ae2a8e2e..cf0d4b0b89 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version0/ChannelCodecs0.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version0/ChannelCodecs0.scala @@ -128,7 +128,7 @@ private[channel] object ChannelCodecs0 { val inputInfoCodec: Codec[InputInfo] = ( ("outPoint" | outPointCodec) :: ("txOut" | txOutCodec) :: - ("redeemScript" | varsizebinarydata)).as[InputInfo].decodeOnly + ("redeemScript" | varsizebinarydata)).as[InputInfo.SegwitInput].upcast[InputInfo].decodeOnly private val defaultConfirmationTarget: Codec[ConfirmationTarget.Absolute] = provide(ConfirmationTarget.Absolute(BlockHeight(0))) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version1/ChannelCodecs1.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version1/ChannelCodecs1.scala index 1f75e8242b..c9424ddd48 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version1/ChannelCodecs1.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/internal/channel/version1/ChannelCodecs1.scala @@ -100,7 +100,7 @@ private[channel] object ChannelCodecs1 { val inputInfoCodec: Codec[InputInfo] = ( ("outPoint" | outPointCodec) :: ("txOut" | txOutCodec) :: - ("redeemScript" | lengthDelimited(bytes))).as[InputInfo] + ("redeemScript" | lengthDelimited(bytes))).as[InputInfo.SegwitInput].upcast[InputInfo].decodeOnly private val defaultConfirmationTarget: Codec[ConfirmationTarget.Absolute] = provide(ConfirmationTarget.Absolute(BlockHeight(0))) 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 8d49b376f9..1923e8064f 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 @@ -104,7 +104,7 @@ private[channel] object ChannelCodecs2 { val inputInfoCodec: Codec[InputInfo] = ( ("outPoint" | outPointCodec) :: ("txOut" | txOutCodec) :: - ("redeemScript" | lengthDelimited(bytes))).as[InputInfo] + ("redeemScript" | lengthDelimited(bytes))).as[InputInfo.SegwitInput].upcast[InputInfo].decodeOnly val outputInfoCodec: Codec[OutputInfo] = ( ("index" | uint32) :: 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 a3a98f7d0e..ccacae297c 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 @@ -116,7 +116,7 @@ private[channel] object ChannelCodecs3 { val inputInfoCodec: Codec[InputInfo] = ( ("outPoint" | outPointCodec) :: ("txOut" | txOutCodec) :: - ("redeemScript" | lengthDelimited(bytes))).as[InputInfo] + ("redeemScript" | lengthDelimited(bytes))).as[InputInfo.SegwitInput].upcast[InputInfo].decodeOnly val outputInfoCodec: Codec[OutputInfo] = ( ("index" | uint32) :: 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 afcfbc4cbd..182c0df1ce 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 @@ -109,10 +109,11 @@ private[channel] object ChannelCodecs4 { val txCodec: Codec[Transaction] = lengthDelimited(bytes.xmap(d => Transaction.read(d.toArray), d => Transaction.write(d))) + // all v4-encoded channels use segwit inputs, support for taproot inputs will be added later in v5 codecs val inputInfoCodec: Codec[InputInfo] = ( ("outPoint" | outPointCodec) :: ("txOut" | txOutCodec) :: - ("redeemScript" | lengthDelimited(bytes))).as[InputInfo] + ("redeemScript" | lengthDelimited(bytes))).as[InputInfo.SegwitInput].upcast[InputInfo] val outputInfoCodec: Codec[OutputInfo] = ( ("index" | uint32) :: diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala index d3f4fcdbde..336ce4cc23 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala @@ -209,9 +209,9 @@ trait TestVectorsSpec extends AnyFunSuite with Logging { remotePaymentBasePoint = Remote.payment_basepoint, localIsChannelOpener = true, outputs = outputs) - val local_sig = Transactions.sign(tx, Local.funding_privkey, TxOwner.Local, commitmentFormat) + val local_sig = tx.sign(Local.funding_privkey, TxOwner.Local, commitmentFormat) logger.info(s"# local_signature = ${Scripts.der(local_sig).dropRight(1).toHex}") - val remote_sig = Transactions.sign(tx, Remote.funding_privkey, TxOwner.Remote, commitmentFormat) + val remote_sig = tx.sign(Remote.funding_privkey, TxOwner.Remote, commitmentFormat) logger.info(s"remote_signature: ${Scripts.der(remote_sig).dropRight(1).toHex}") Transactions.addSigs(tx, Local.funding_pubkey, Remote.funding_pubkey, local_sig, remote_sig) } @@ -248,9 +248,9 @@ trait TestVectorsSpec extends AnyFunSuite with Logging { val signedTxs = htlcTxs.collect { case tx: HtlcSuccessTx => - val localSig = Transactions.sign(tx, Local.htlc_privkey, TxOwner.Local, commitmentFormat) - val remoteSig = Transactions.sign(tx, Remote.htlc_privkey, TxOwner.Remote, commitmentFormat) - val htlcIndex = htlcScripts.indexOf(Script.parse(tx.input.redeemScript)) + val localSig = tx.sign(Local.htlc_privkey, TxOwner.Local, commitmentFormat) + val remoteSig = tx.sign(Remote.htlc_privkey, TxOwner.Remote, commitmentFormat) + val htlcIndex = htlcScripts.indexOf(Script.parse(tx.input.asInstanceOf[InputInfo.SegwitInput].redeemScript)) val preimage = paymentPreimages.find(p => Crypto.sha256(p) == tx.paymentHash).get val tx1 = Transactions.addSigs(tx, localSig, remoteSig, preimage, commitmentFormat) Transaction.correctlySpends(tx1.tx, Seq(commitTx.tx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) @@ -260,9 +260,9 @@ trait TestVectorsSpec extends AnyFunSuite with Logging { logger.info(s"htlc_success_tx (htlc #$htlcIndex): ${tx1.tx}") tx1 case tx: HtlcTimeoutTx => - val localSig = Transactions.sign(tx, Local.htlc_privkey, TxOwner.Local, commitmentFormat) - val remoteSig = Transactions.sign(tx, Remote.htlc_privkey, TxOwner.Remote, commitmentFormat) - val htlcIndex = htlcScripts.indexOf(Script.parse(tx.input.redeemScript)) + val localSig = tx.sign(Local.htlc_privkey, TxOwner.Local, commitmentFormat) + val remoteSig = tx.sign(Remote.htlc_privkey, TxOwner.Remote, commitmentFormat) + val htlcIndex = htlcScripts.indexOf(Script.parse(tx.input.asInstanceOf[InputInfo.SegwitInput].redeemScript)) val tx1 = Transactions.addSigs(tx, localSig, remoteSig, commitmentFormat) Transaction.correctlySpends(tx1.tx, Seq(commitTx.tx), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS) logger.info(s"# signature for output #${tx.input.outPoint.index} (htlc-timeout for htlc #$htlcIndex)") 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 951c62146b..88d7898063 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 @@ -290,8 +290,8 @@ class TransactionsSpec extends AnyFunSuite with Logging { val commitTxNumber = 0x404142434445L val commitTx = { val txInfo = makeCommitTx(commitInput, commitTxNumber, localPaymentPriv.publicKey, remotePaymentPriv.publicKey, localIsChannelOpener = true, outputs) - val localSig = Transactions.sign(txInfo, localPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) - val remoteSig = Transactions.sign(txInfo, remotePaymentPriv, TxOwner.Remote, DefaultCommitmentFormat) + val localSig = txInfo.sign(localPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) + val remoteSig = txInfo.sign(remotePaymentPriv, TxOwner.Remote, DefaultCommitmentFormat) Transactions.addSigs(txInfo, localFundingPriv.publicKey, remoteFundingPriv.publicKey, localSig, remoteSig) } @@ -317,8 +317,8 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // either party spends local->remote htlc output with htlc timeout tx for (htlcTimeoutTx <- htlcTimeoutTxs) { - val localSig = sign(htlcTimeoutTx, localHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) - val remoteSig = sign(htlcTimeoutTx, remoteHtlcPriv, TxOwner.Remote, DefaultCommitmentFormat) + val localSig = htlcTimeoutTx.sign(localHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) + val remoteSig = htlcTimeoutTx.sign(remoteHtlcPriv, TxOwner.Remote, DefaultCommitmentFormat) val signed = addSigs(htlcTimeoutTx, localSig, remoteSig, DefaultCommitmentFormat) assert(checkSpendable(signed).isSuccess) } @@ -326,7 +326,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // local spends delayed output of htlc1 timeout tx val Right(htlcDelayed) = makeHtlcDelayedTx(htlcTimeoutTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(htlcDelayed, localDelayedPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) + val localSig = htlcDelayed.sign(localDelayedPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) val signedTx = addSigs(htlcDelayed, localSig) assert(checkSpendable(signedTx).isSuccess) // local can't claim delayed output of htlc3 timeout tx because it is below the dust limit @@ -337,7 +337,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { // remote spends local->remote htlc1/htlc3 output directly in case of success for ((htlc, paymentPreimage) <- (htlc1, paymentPreimage1) :: (htlc3, paymentPreimage3) :: Nil) { val Right(claimHtlcSuccessTx) = makeClaimHtlcSuccessTx(commitTx.tx, outputs, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw, DefaultCommitmentFormat) - val localSig = sign(claimHtlcSuccessTx, remoteHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) + val localSig = claimHtlcSuccessTx.sign(remoteHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(claimHtlcSuccessTx, localSig, paymentPreimage) assert(checkSpendable(signed).isSuccess) } @@ -345,18 +345,18 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // local spends remote->local htlc2/htlc4 output with htlc success tx using payment preimage for ((htlcSuccessTx, paymentPreimage) <- (htlcSuccessTxs(1), paymentPreimage2) :: (htlcSuccessTxs(0), paymentPreimage4) :: Nil) { - val localSig = sign(htlcSuccessTx, localHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) - val remoteSig = sign(htlcSuccessTx, remoteHtlcPriv, TxOwner.Remote, DefaultCommitmentFormat) + val localSig = htlcSuccessTx.sign(localHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) + val remoteSig = htlcSuccessTx.sign(remoteHtlcPriv, TxOwner.Remote, DefaultCommitmentFormat) val signedTx = addSigs(htlcSuccessTx, localSig, remoteSig, paymentPreimage, DefaultCommitmentFormat) assert(checkSpendable(signedTx).isSuccess) // check remote sig - assert(checkSig(htlcSuccessTx, remoteSig, remoteHtlcPriv.publicKey, TxOwner.Remote, DefaultCommitmentFormat)) + assert(htlcSuccessTx.checkSig(remoteSig, remoteHtlcPriv.publicKey, TxOwner.Remote, DefaultCommitmentFormat)) } } { // local spends delayed output of htlc2 success tx val Right(htlcDelayed) = makeHtlcDelayedTx(htlcSuccessTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(htlcDelayed, localDelayedPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) + val localSig = htlcDelayed.sign(localDelayedPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) val signedTx = addSigs(htlcDelayed, localSig) assert(checkSpendable(signedTx).isSuccess) // local can't claim delayed output of htlc4 success tx because it is below the dust limit @@ -366,35 +366,35 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // local spends main delayed output val Right(claimMainOutputTx) = makeClaimLocalDelayedOutputTx(commitTx.tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(claimMainOutputTx, localDelayedPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) + val localSig = claimMainOutputTx.sign(localDelayedPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) val signedTx = addSigs(claimMainOutputTx, localSig) assert(checkSpendable(signedTx).isSuccess) } { // remote spends main output val Right(claimP2WPKHOutputTx) = makeClaimP2WPKHOutputTx(commitTx.tx, localDustLimit, remotePaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(claimP2WPKHOutputTx, remotePaymentPriv, TxOwner.Local, DefaultCommitmentFormat) + val localSig = claimP2WPKHOutputTx.sign(remotePaymentPriv, TxOwner.Local, DefaultCommitmentFormat) val signedTx = addSigs(claimP2WPKHOutputTx, remotePaymentPriv.publicKey, localSig) assert(checkSpendable(signedTx).isSuccess) } { // remote spends remote->local htlc output directly in case of timeout val Right(claimHtlcTimeoutTx) = makeClaimHtlcTimeoutTx(commitTx.tx, outputs, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc2, feeratePerKw, DefaultCommitmentFormat) - val localSig = sign(claimHtlcTimeoutTx, remoteHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) + val localSig = claimHtlcTimeoutTx.sign(remoteHtlcPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(claimHtlcTimeoutTx, localSig) assert(checkSpendable(signed).isSuccess) } { // remote spends local main delayed output with revocation key val Right(mainPenaltyTx) = makeMainPenaltyTx(commitTx.tx, localDustLimit, localRevocationPriv.publicKey, finalPubKeyScript, toLocalDelay, localDelayedPaymentPriv.publicKey, feeratePerKw) - val sig = sign(mainPenaltyTx, localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) + val sig = mainPenaltyTx.sign(localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(mainPenaltyTx, sig) assert(checkSpendable(signed).isSuccess) } { // remote spends htlc1's htlc-timeout tx with revocation key val Seq(Right(claimHtlcDelayedPenaltyTx)) = makeClaimHtlcDelayedOutputPenaltyTxs(htlcTimeoutTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val sig = sign(claimHtlcDelayedPenaltyTx, localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) + val sig = claimHtlcDelayedPenaltyTx.sign(localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(claimHtlcDelayedPenaltyTx, sig) assert(checkSpendable(signed).isSuccess) // remote can't claim revoked output of htlc3's htlc-timeout tx because it is below the dust limit @@ -409,14 +409,14 @@ class TransactionsSpec extends AnyFunSuite with Logging { case _ => false }.map(_._2) val Right(htlcPenaltyTx) = makeHtlcPenaltyTx(commitTx.tx, htlcOutputIndex, script, localDustLimit, finalPubKeyScript, feeratePerKw) - val sig = sign(htlcPenaltyTx, localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) + val sig = htlcPenaltyTx.sign(localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(htlcPenaltyTx, sig, localRevocationPriv.publicKey) assert(checkSpendable(signed).isSuccess) } { // remote spends htlc2's htlc-success tx with revocation key val Seq(Right(claimHtlcDelayedPenaltyTx)) = makeClaimHtlcDelayedOutputPenaltyTxs(htlcSuccessTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val sig = sign(claimHtlcDelayedPenaltyTx, localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) + val sig = claimHtlcDelayedPenaltyTx.sign(localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(claimHtlcDelayedPenaltyTx, sig) assert(checkSpendable(signed).isSuccess) // remote can't claim revoked output of htlc4's htlc-success tx because it is below the dust limit @@ -431,7 +431,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { case _ => false }.map(_._2) val Right(htlcPenaltyTx) = makeHtlcPenaltyTx(commitTx.tx, htlcOutputIndex, script, localDustLimit, finalPubKeyScript, feeratePerKw) - val sig = sign(htlcPenaltyTx, localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) + val sig = htlcPenaltyTx.sign(localRevocationPriv, TxOwner.Local, DefaultCommitmentFormat) val signed = addSigs(htlcPenaltyTx, sig, localRevocationPriv.publicKey) assert(checkSpendable(signed).isSuccess) } @@ -527,8 +527,8 @@ class TransactionsSpec extends AnyFunSuite with Logging { val commitTxNumber = 0x404142434445L val outputs = makeCommitTxOutputs(localPaysCommitTxFees = true, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, remotePaymentPriv.publicKey, localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localFundingPriv.publicKey, remoteFundingPriv.publicKey, spec, UnsafeLegacyAnchorOutputsCommitmentFormat) val txInfo = makeCommitTx(commitInput, commitTxNumber, localPaymentPriv.publicKey, remotePaymentPriv.publicKey, localIsChannelOpener = true, outputs) - val localSig = Transactions.sign(txInfo, localPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) - val remoteSig = Transactions.sign(txInfo, remotePaymentPriv, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = txInfo.sign(localPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val remoteSig = txInfo.sign(remotePaymentPriv, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat) val commitTx = Transactions.addSigs(txInfo, localFundingPriv.publicKey, remoteFundingPriv.publicKey, localSig, remoteSig) val htlcTxs = makeHtlcTxs(commitTx.tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, spec.htlcTxFeerate(UnsafeLegacyAnchorOutputsCommitmentFormat), outputs, UnsafeLegacyAnchorOutputsCommitmentFormat) @@ -563,7 +563,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // local spends main delayed output val Right(claimMainOutputTx) = makeClaimLocalDelayedOutputTx(commitTx.tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(claimMainOutputTx, localDelayedPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = claimMainOutputTx.sign(localDelayedPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(claimMainOutputTx, localSig) assert(checkSpendable(signedTx).isSuccess) } @@ -575,7 +575,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // remote spends main delayed output val Right(claimRemoteDelayedOutputTx) = makeClaimRemoteDelayedOutputTx(commitTx.tx, localDustLimit, remotePaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(claimRemoteDelayedOutputTx, remotePaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = claimRemoteDelayedOutputTx.sign(remotePaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(claimRemoteDelayedOutputTx, localSig) assert(checkSpendable(signedTx).isSuccess) } @@ -583,7 +583,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { // local spends local anchor val Right(claimAnchorOutputTx) = makeClaimLocalAnchorOutputTx(commitTx.tx, localFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(0))) assert(checkSpendable(claimAnchorOutputTx).isFailure) - val localSig = sign(claimAnchorOutputTx, localFundingPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = claimAnchorOutputTx.sign(localFundingPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(claimAnchorOutputTx, localSig) assert(checkSpendable(signedTx).isSuccess) } @@ -591,28 +591,28 @@ class TransactionsSpec extends AnyFunSuite with Logging { // remote spends remote anchor val Right(claimAnchorOutputTx) = makeClaimLocalAnchorOutputTx(commitTx.tx, remoteFundingPriv.publicKey, ConfirmationTarget.Absolute(BlockHeight(0))) assert(checkSpendable(claimAnchorOutputTx).isFailure) - val localSig = sign(claimAnchorOutputTx, remoteFundingPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = claimAnchorOutputTx.sign(remoteFundingPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(claimAnchorOutputTx, localSig) assert(checkSpendable(signedTx).isSuccess) } { // remote spends local main delayed output with revocation key val Right(mainPenaltyTx) = makeMainPenaltyTx(commitTx.tx, localDustLimit, localRevocationPriv.publicKey, finalPubKeyScript, toLocalDelay, localDelayedPaymentPriv.publicKey, feeratePerKw) - val sig = sign(mainPenaltyTx, localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val sig = mainPenaltyTx.sign(localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(mainPenaltyTx, sig) assert(checkSpendable(signed).isSuccess) } { // local spends received htlc with HTLC-timeout tx for (htlcTimeoutTx <- htlcTimeoutTxs) { - val localSig = sign(htlcTimeoutTx, localHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) - val remoteSig = sign(htlcTimeoutTx, remoteHtlcPriv, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = htlcTimeoutTx.sign(localHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val remoteSig = htlcTimeoutTx.sign(remoteHtlcPriv, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(htlcTimeoutTx, localSig, remoteSig, UnsafeLegacyAnchorOutputsCommitmentFormat) assert(checkSpendable(signedTx).isSuccess) // local detects when remote doesn't use the right sighash flags val invalidSighash = Seq(SIGHASH_ALL, SIGHASH_ALL | SIGHASH_ANYONECANPAY, SIGHASH_SINGLE, SIGHASH_NONE) for (sighash <- invalidSighash) { - val invalidRemoteSig = sign(htlcTimeoutTx, remoteHtlcPriv, sighash) + val invalidRemoteSig = htlcTimeoutTx.sign(remoteHtlcPriv, sighash) val invalidTx = addSigs(htlcTimeoutTx, localSig, invalidRemoteSig, UnsafeLegacyAnchorOutputsCommitmentFormat) assert(checkSpendable(invalidTx).isFailure) } @@ -621,7 +621,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // local spends delayed output of htlc1 timeout tx val Right(htlcDelayed) = makeHtlcDelayedTx(htlcTimeoutTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val localSig = sign(htlcDelayed, localDelayedPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = htlcDelayed.sign(localDelayedPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(htlcDelayed, localSig) assert(checkSpendable(signedTx).isSuccess) // local can't claim delayed output of htlc3 timeout tx because it is below the dust limit @@ -631,19 +631,19 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // local spends offered htlc with HTLC-success tx for ((htlcSuccessTx, paymentPreimage) <- (htlcSuccessTxs(0), paymentPreimage4) :: (htlcSuccessTxs(1), paymentPreimage2) :: (htlcSuccessTxs(2), paymentPreimage2) :: Nil) { - val localSig = sign(htlcSuccessTx, localHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) - val remoteSig = sign(htlcSuccessTx, remoteHtlcPriv, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = htlcSuccessTx.sign(localHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val remoteSig = htlcSuccessTx.sign(remoteHtlcPriv, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(htlcSuccessTx, localSig, remoteSig, paymentPreimage, UnsafeLegacyAnchorOutputsCommitmentFormat) assert(checkSpendable(signedTx).isSuccess) // check remote sig - assert(checkSig(htlcSuccessTx, remoteSig, remoteHtlcPriv.publicKey, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat)) + assert(htlcSuccessTx.checkSig(remoteSig, remoteHtlcPriv.publicKey, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat)) // local detects when remote doesn't use the right sighash flags val invalidSighash = Seq(SIGHASH_ALL, SIGHASH_ALL | SIGHASH_ANYONECANPAY, SIGHASH_SINGLE, SIGHASH_NONE) for (sighash <- invalidSighash) { - val invalidRemoteSig = sign(htlcSuccessTx, remoteHtlcPriv, sighash) + val invalidRemoteSig = htlcSuccessTx.sign(remoteHtlcPriv, sighash) val invalidTx = addSigs(htlcSuccessTx, localSig, invalidRemoteSig, paymentPreimage, UnsafeLegacyAnchorOutputsCommitmentFormat) assert(checkSpendable(invalidTx).isFailure) - assert(!checkSig(invalidTx, invalidRemoteSig, remoteHtlcPriv.publicKey, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat)) + assert(!invalidTx.checkSig(invalidRemoteSig, remoteHtlcPriv.publicKey, TxOwner.Remote, UnsafeLegacyAnchorOutputsCommitmentFormat)) } } } @@ -652,7 +652,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { val Right(htlcDelayedA) = makeHtlcDelayedTx(htlcSuccessTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) val Right(htlcDelayedB) = makeHtlcDelayedTx(htlcSuccessTxs(2).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) for (htlcDelayed <- Seq(htlcDelayedA, htlcDelayedB)) { - val localSig = sign(htlcDelayed, localDelayedPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = htlcDelayed.sign(localDelayedPaymentPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signedTx = addSigs(htlcDelayed, localSig) assert(checkSpendable(signedTx).isSuccess) } @@ -664,7 +664,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { // remote spends local->remote htlc outputs directly in case of success for ((htlc, paymentPreimage) <- (htlc1, paymentPreimage1) :: (htlc3, paymentPreimage3) :: Nil) { val Right(claimHtlcSuccessTx) = makeClaimHtlcSuccessTx(commitTx.tx, commitTxOutputs, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw, UnsafeLegacyAnchorOutputsCommitmentFormat) - val localSig = sign(claimHtlcSuccessTx, remoteHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = claimHtlcSuccessTx.sign(remoteHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(claimHtlcSuccessTx, localSig, paymentPreimage) assert(checkSpendable(signed).isSuccess) } @@ -672,7 +672,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { { // remote spends htlc1's htlc-timeout tx with revocation key val Seq(Right(claimHtlcDelayedPenaltyTx)) = makeClaimHtlcDelayedOutputPenaltyTxs(htlcTimeoutTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) - val sig = sign(claimHtlcDelayedPenaltyTx, localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val sig = claimHtlcDelayedPenaltyTx.sign(localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(claimHtlcDelayedPenaltyTx, sig) assert(checkSpendable(signed).isSuccess) // remote can't claim revoked output of htlc3's htlc-timeout tx because it is below the dust limit @@ -683,7 +683,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { // remote spends remote->local htlc output directly in case of timeout for (htlc <- Seq(htlc2a, htlc2b)) { val Right(claimHtlcTimeoutTx) = makeClaimHtlcTimeoutTx(commitTx.tx, commitTxOutputs, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw, UnsafeLegacyAnchorOutputsCommitmentFormat) - val localSig = sign(claimHtlcTimeoutTx, remoteHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val localSig = claimHtlcTimeoutTx.sign(remoteHtlcPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(claimHtlcTimeoutTx, localSig) assert(checkSpendable(signed).isSuccess) } @@ -693,7 +693,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { val Seq(Right(claimHtlcDelayedPenaltyTxA)) = makeClaimHtlcDelayedOutputPenaltyTxs(htlcSuccessTxs(1).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) val Seq(Right(claimHtlcDelayedPenaltyTxB)) = makeClaimHtlcDelayedOutputPenaltyTxs(htlcSuccessTxs(2).tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, finalPubKeyScript, feeratePerKw) for (claimHtlcSuccessPenaltyTx <- Seq(claimHtlcDelayedPenaltyTxA, claimHtlcDelayedPenaltyTxB)) { - val sig = sign(claimHtlcSuccessPenaltyTx, localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val sig = claimHtlcSuccessPenaltyTx.sign(localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(claimHtlcSuccessPenaltyTx, sig) assert(checkSpendable(signed).isSuccess) } @@ -723,7 +723,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { case _ => false }.map(_._2) val Right(htlcPenaltyTx) = makeHtlcPenaltyTx(commitTx.tx, htlcOutputIndex, script, localDustLimit, finalPubKeyScript, feeratePerKw) - val sig = sign(htlcPenaltyTx, localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val sig = htlcPenaltyTx.sign(localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(htlcPenaltyTx, sig, localRevocationPriv.publicKey) assert(checkSpendable(signed).isSuccess) } @@ -736,7 +736,7 @@ class TransactionsSpec extends AnyFunSuite with Logging { case _ => false }.map(_._2) val Right(htlcPenaltyTx) = makeHtlcPenaltyTx(commitTx.tx, htlcOutputIndex, script, localDustLimit, finalPubKeyScript, feeratePerKw) - val sig = sign(htlcPenaltyTx, localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) + val sig = htlcPenaltyTx.sign(localRevocationPriv, TxOwner.Local, UnsafeLegacyAnchorOutputsCommitmentFormat) val signed = addSigs(htlcPenaltyTx, sig, localRevocationPriv.publicKey) assert(checkSpendable(signed).isSuccess) } @@ -782,8 +782,8 @@ class TransactionsSpec extends AnyFunSuite with Logging { val (commitTx, outputs, htlcTxs) = { val outputs = makeCommitTxOutputs(localPaysCommitTxFees = true, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, remotePaymentPriv.publicKey, localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localFundingPriv.publicKey, remoteFundingPriv.publicKey, spec, DefaultCommitmentFormat) val txInfo = makeCommitTx(commitInput, commitTxNumber, localPaymentPriv.publicKey, remotePaymentPriv.publicKey, localIsChannelOpener = true, outputs) - val localSig = Transactions.sign(txInfo, localPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) - val remoteSig = Transactions.sign(txInfo, remotePaymentPriv, TxOwner.Remote, DefaultCommitmentFormat) + val localSig = txInfo.sign(localPaymentPriv, TxOwner.Local, DefaultCommitmentFormat) + val remoteSig = txInfo.sign(remotePaymentPriv, TxOwner.Remote, DefaultCommitmentFormat) val commitTx = Transactions.addSigs(txInfo, localFundingPriv.publicKey, remoteFundingPriv.publicKey, localSig, remoteSig) val htlcTxs = makeHtlcTxs(commitTx.tx, localDustLimit, localRevocationPriv.publicKey, toLocalDelay, localDelayedPaymentPriv.publicKey, feeratePerKw, outputs, DefaultCommitmentFormat) (commitTx, outputs, htlcTxs) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala index eb9424e830..1c4620db64 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/internal/channel/ChannelCodecsSpec.scala @@ -242,7 +242,7 @@ class ChannelCodecsSpec extends AnyFunSuite { assert(newnormal.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx.txIn.forall(_.witness.stack.isEmpty)) assert(newnormal.commitments.latest.localCommit.htlcTxsAndRemoteSigs.forall(_.htlcTx.tx.txIn.forall(_.witness.stack.isEmpty))) // make sure that we have extracted the remote sig of the local tx - Transactions.checkSig(newnormal.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx, newnormal.commitments.latest.localCommit.commitTxAndRemoteSig.remoteSig, newnormal.commitments.remoteNodeId, TxOwner.Remote, newnormal.commitments.params.commitmentFormat) + newnormal.commitments.latest.localCommit.commitTxAndRemoteSig.commitTx.checkSig(newnormal.commitments.latest.localCommit.commitTxAndRemoteSig.remoteSig, newnormal.commitments.remoteNodeId, TxOwner.Remote, newnormal.commitments.params.commitmentFormat) } }