From 0c14fc961bff66b737a5125084978843b12852e9 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 1 Jul 2022 16:45:41 -0500 Subject: [PATCH] Add test case for `SIGHASH_ALL_ANYONECANPAY` in taproot (#4442) * Add test case for SIGHASH_ALL_ANYONECANPAY in taproot * Remove spendingTx comment --- .../TransactionSignatureSerializerTest.scala | 55 ++++++++++++++++--- .../TransactionSignatureSerializer.scala | 28 +++++++--- 2 files changed, 67 insertions(+), 16 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 6a2641292c..2af0b7a457 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 @@ -561,7 +561,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { val outputMap: Map[TransactionOutPoint, TransactionOutput] = spendingTx.inputs.map(_.previousOutput).zip(Vector(prevOutput)).toMap - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -616,7 +616,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { val outputMap: Map[TransactionOutPoint, TransactionOutput] = spendingTx.inputs.map(_.previousOutput).zip(prevOutputs).toMap - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -688,7 +688,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { .toWitnessTx(spendingTx) .updateWitness(inputIndex.toInt, witness) - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -731,7 +731,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { .toWitnessTx(spendingTx) .updateWitness(inputIndex.toInt, witness) - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -782,7 +782,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { .toWitnessTx(spendingTx) .updateWitness(inputIndex.toInt, witness) - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -829,7 +829,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { .toWitnessTx(spendingTx) .updateWitness(inputIndex.toInt, witness) - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -920,7 +920,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { .toWitnessTx(spendingTx) .updateWitness(inputIndex.toInt, witness) - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -961,7 +961,7 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { .toWitnessTx(spendingTx) .updateWitness(inputIndex.toInt, witness) - val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, //spendingTx, + val taprootTxSigComponent = TaprootTxSigComponent(witnessTx, inputIndex, outputMap, Policy.standardFlags) @@ -980,4 +980,43 @@ class TransactionSignatureSerializerTest extends BitcoinSUnitTest { assert(serialize.toHex == expected) } + + it must "serialize a taproot keyspend SIGHASH_ALL_ANYONECANSPEND" in { + val expected = + "00816c780499da010000a0e6c05b4d684eeb145d9225f15319226c5fd78617f5db646630c0584662a6a3008c033b67f78dfe0867489e0e5a032f24d14ee3c57637c22e71701fd79b64012ad200000094037f010000000022512057c1162a56ec9db80a8eb342634f613c8d990bf305925df7ddb85b356ce8f0bb7138b5c8" + + val spendingTxHex = + "6c780499018c033b67f78dfe0867489e0e5a032f24d14ee3c57637c22e71701fd79b64012ad2000000007138b5c80168666e0000000000160014e7f8b1de86947bf379378ebd4368d37361fac307da010000" + val spendingTx = Transaction.fromHex(spendingTxHex) + val inputIndex = UInt32.zero + val prevout = TransactionOutput.fromHex( + "94037f010000000022512057c1162a56ec9db80a8eb342634f613c8d990bf305925df7ddb85b356ce8f0bb") + val prevOutputs = Vector(prevout) + val outputMap: Map[TransactionOutPoint, TransactionOutput] = + spendingTx.inputs.map(_.previousOutput).zip(prevOutputs).toMap + val witnessStackHex = Vector( + "228187c314a903fe94c1c8260c243e0949a3233d404a58f90cd991a44f5dad4d89bd5763525b437a10a973abd8eb99adcfec9e2865fa235fa4d6af82c427f17a81") + 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, + inputIndex, + outputMap, + Policy.standardFlags) + + val taprootOptions = TaprootSerializationOptions.empty + + val serialize = TransactionSignatureSerializer.serializeForSignature( + taprootTxSigComponent, + HashType.sigHashAllAnyoneCanPay, + 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 d82fa9c470..e301f965e7 100644 --- a/core/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala +++ b/core/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala @@ -248,6 +248,8 @@ sealed abstract class TransactionSignatureSerializer { val isNotAnyoneCanPay = !HashType.isAnyoneCanPay(hashType) val isNotSigHashSingle = !HashType.isSigHashSingle(hashType.num) val isNotSigHashNone = !HashType.isSigHashNone(hashType.num) + val isSigHashAllAnyoneCanPay = + HashType.isSigHashAllAnyoneCanPay(hashType.num) val extFlag = taprootSigVersion match { case SigVersionTaprootKeySpend => 0.toByte @@ -265,6 +267,7 @@ sealed abstract class TransactionSignatureSerializer { val b = spendingTransaction.inputs(inputIndex.toInt).previousOutput b.bytes } + val amounts = { if (isNotAnyoneCanPay) { val b = BytesUtil.toByteVector(outputs.map(_.value)) @@ -282,6 +285,7 @@ sealed abstract class TransactionSignatureSerializer { b.bytes } } + val sequenceHash: ByteVector = if (isNotAnyoneCanPay) { val sequences = spendingTransaction.inputs.map(_.sequence) @@ -314,7 +318,6 @@ sealed abstract class TransactionSignatureSerializer { .bytes hash } else ByteVector.empty - val haveAnnex: Boolean = taprootOptions.haveAnnex val annexByte = if (haveAnnex) 1.toByte else 0.toByte @@ -362,14 +365,23 @@ sealed abstract class TransactionSignatureSerializer { } } else { - //different ordering if we use SIGHASH_ANYONECANPAY - epoch ++ ByteVector.fromByte( - hashType.byte) ++ version ++ locktimeBytes ++ ByteVector.fromByte( - spendType) ++ - outPointHash ++ amounts ++ spentSPKs ++ - sequenceHash ++ annexBytes ++ outputHash ++ tapScriptBytes + if (isSigHashAllAnyoneCanPay) { + //different ordering if we use SIGHASH_ANYONECANPAY + epoch ++ ByteVector + .fromByte( + hashType.byte) ++ version ++ locktimeBytes ++ outputHash ++ + ByteVector.fromByte( + spendType) ++ outPointHash ++ amounts ++ spentSPKs ++ + sequenceHash ++ annexBytes ++ tapScriptBytes + } else { + //different ordering if we use SIGHASH_ANYONECANPAY + epoch ++ ByteVector.fromByte( + hashType.byte) ++ version ++ locktimeBytes ++ ByteVector + .fromByte(spendType) ++ + outPointHash ++ amounts ++ spentSPKs ++ + sequenceHash ++ annexBytes ++ outputHash ++ tapScriptBytes + } } - } result }