From 245815b4d4708367eb1b7edb4c9cb594a30a4ade Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 6 May 2016 16:41:59 -0500 Subject: [PATCH] Fixing bug with signature serialization - if we have more inputs than outputs & we are using SIGHASH_SINGLE the errorHash is signed --- .../TransactionSignatureSerializer.scala | 22 ++++++++++++++++--- .../transaction/TransactionTest.scala | 6 ++--- .../CoreTransactionTestCase.scala | 1 + 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala b/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala index 74950323e7..62754a94bc 100644 --- a/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala +++ b/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala @@ -47,6 +47,11 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit * @return */ def serializeForSignature(spendingTransaction : Transaction, inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = { + logger.debug("Serializing for signature") + + + + // Clear input scripts in preparation for signing. If we're signing a fresh // transaction that step isn't very helpful, but it doesn't add much cost relative to the actual // EC math so we'll do it anyway. @@ -147,9 +152,20 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit * @param hashType * @return */ - def hashForSignature(spendingTransction : Transaction, inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = { - val serializedTxForSignature = serializeForSignature(spendingTransction,inputIndex,script,hashType) - CryptoUtil.doubleSHA256(serializedTxForSignature) + def hashForSignature(spendingTransaction : Transaction, inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = { + //these first two checks are in accordance with behavior in bitcoin core + //https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L1112-L1123 + if (inputIndex >= spendingTransaction.inputs.size ) { + logger.warn("Our inputIndex is out of the range of the inputs in the spending transaction") + errorHash + } + else if(hashType == SIGHASH_SINGLE && inputIndex >= spendingTransaction.outputs.size) { + logger.warn("When we have a SIGHASH_SINGLE we cannot have more inputs than outputs") + errorHash + } else { + val serializedTxForSignature = serializeForSignature(spendingTransaction,inputIndex,script,hashType) + CryptoUtil.doubleSHA256(serializedTxForSignature) + } } /** diff --git a/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala b/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala index 4e2159f23a..148cc1853a 100644 --- a/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala +++ b/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala @@ -49,14 +49,14 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger { //use this to represent a single test case from script_valid.json -/* val lines = + val lines = """ |[ |[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]], |"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"] |] - """.stripMargin*/ - val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close() + """.stripMargin + //val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close() val json = lines.parseJson val testCasesOpt : Seq[Option[CoreTransactionTestCase]] = json.convertTo[Seq[Option[CoreTransactionTestCase]]] val testCases : Seq[CoreTransactionTestCase] = testCasesOpt.flatten diff --git a/src/test/scala/org/bitcoins/protocol/transaction/testprotocol/CoreTransactionTestCase.scala b/src/test/scala/org/bitcoins/protocol/transaction/testprotocol/CoreTransactionTestCase.scala index e9e82ee02c..fc26b59c1d 100644 --- a/src/test/scala/org/bitcoins/protocol/transaction/testprotocol/CoreTransactionTestCase.scala +++ b/src/test/scala/org/bitcoins/protocol/transaction/testprotocol/CoreTransactionTestCase.scala @@ -17,6 +17,7 @@ trait CoreTransactionTestCase { def scriptPubKeys : Seq[ScriptPubKey] = creditingTxsInfo.map(_._2) def creditingTxsInfo : Seq[(TransactionOutPoint, ScriptPubKey)] + def spendingTx : Transaction def flags : Seq[ScriptFlag]