From 3122e1d0f8f7170882cd7e80eafa21e7160bd8db Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 1 Jul 2022 10:24:09 -0500 Subject: [PATCH] Fix bug in taproot SIGHASH_ANYONECANPAY_SINGLE impl (#4440) --- .../TransactionSignatureSerializerTest.scala | 51 +++++++++++++++++-- .../TransactionSignatureSerializer.scala | 4 +- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala b/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala index 6ea937100b..6a2641292c 100644 --- a/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala +++ b/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala @@ -499,11 +499,6 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { signInfo.hashType, taprootOptions = TaprootSerializationOptions.empty) - if (oldHash != newHash) { - println(oldHash.hex) - println(newHash.hex) - } - oldHash == newHash } } @@ -939,4 +934,50 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { assert(serialize.toHex == expected) } + + it must "serialize a taprooot keypsend SIGHASH_SINGLE_ANYONECANPAY with annex correctly" in { + val expected = + "0083a3c824f5613e694f01ec975c5d145e8cccde50ab0ca8c5b41bfaedd041ea97b05285fff9df00dca26749010000bd2897010000000022512088ffcb569bf1dc1a9e0b22b8cb164c31bcf65e6e95fa113ddbdbbead6e85fedf4aa79981b0d1ad766994166c07455739e8a49adf25972a25046ede7f1573ec61b75beb3f7e93c573a659c28b99050bd63f8e7bd619af0175b784eadf33b52d95d3863c51" + + val spendingTxHex = + "a3c824f501ec975c5d145e8cccde50ab0ca8c5b41bfaedd041ea97b05285fff9df00dca26749010000004aa799810120931801000000001976a914a9afd3de61b334f2bf5db90a343f96ab28c4e8ed88ac613e694f" + val spendingTx = Transaction.fromHex(spendingTxHex) + val inputIndex = UInt32.zero + val prevout = TransactionOutput.fromHex( + "bd2897010000000022512088ffcb569bf1dc1a9e0b22b8cb164c31bcf65e6e95fa113ddbdbbead6e85fedf") + val prevOutputs = Vector(prevout) + val outputMap: Map[TransactionOutPoint, TransactionOutput] = + spendingTx.inputs.map(_.previousOutput).zip(prevOutputs).toMap + val witnessStackHex = Vector( + "f14afc2bf9b4a9f8e4b5e8503e396de9ad12aacf7726fd53dc147978f52bf9435d658cb326f533b39c57af2c8406f5177120eda69e51f52e0fd076040c7d831d83", + "50d8" + ) + val witnessStack = witnessStackHex.map(w => ByteVector.fromValidHex(w)) + + val witness = TaprootKeyPath.fromStack(witnessStack.reverse) + + val witnessTx = + WitnessTransaction + .toWitnessTx(spendingTx) + .updateWitness(inputIndex.toInt, witness) + + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + inputIndex, + outputMap, + Policy.standardFlags) + + val annexHashHex = + "b0d1ad766994166c07455739e8a49adf25972a25046ede7f1573ec61b75beb3f" + val annexHash = Sha256Digest.fromHex(annexHashHex) + val taprootOptions = + TaprootSerializationOptions(tapLeafHashOpt = None, + annexHashOpt = Some(annexHash), + codeSeparatorPosOpt = None) + val serialize = TransactionSignatureSerializer.serializeForSignature( + taprootTxSigComponent, + HashType.sigHashSingleAnyoneCanPay, + taprootOptions) + + assert(serialize.toHex == expected) + } } diff --git a/core/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala b/core/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala index 19cb14f4d8..d82fa9c470 100644 --- a/core/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala +++ b/core/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala @@ -316,6 +316,7 @@ sealed abstract class TransactionSignatureSerializer { } else ByteVector.empty val haveAnnex: Boolean = taprootOptions.haveAnnex + val annexByte = if (haveAnnex) 1.toByte else 0.toByte val spendType: Byte = ((extFlag << 1) + annexByte).toByte @@ -341,6 +342,7 @@ sealed abstract class TransactionSignatureSerializer { } else { ByteVector.empty } + val result = { if (isNotAnyoneCanPay) { if (!isNotSigHashSingle) { @@ -365,7 +367,7 @@ sealed abstract class TransactionSignatureSerializer { hashType.byte) ++ version ++ locktimeBytes ++ ByteVector.fromByte( spendType) ++ outPointHash ++ amounts ++ spentSPKs ++ - sequenceHash ++ outputHash ++ annexBytes ++ tapScriptBytes + sequenceHash ++ annexBytes ++ outputHash ++ tapScriptBytes } }