diff --git a/src/main/scala/org/bitcoins/crypto/TransactionSignatureChecker.scala b/src/main/scala/org/bitcoins/crypto/TransactionSignatureChecker.scala index a108f748f1..eedb173b7d 100644 --- a/src/main/scala/org/bitcoins/crypto/TransactionSignatureChecker.scala +++ b/src/main/scala/org/bitcoins/crypto/TransactionSignatureChecker.scala @@ -53,35 +53,26 @@ trait TransactionSignatureChecker extends BitcoinSLogger { //we do this by setting the scriptPubKey inside of txSignatureComponent to the redeemScript //instead of the p2sh scriptPubKey it was previously //as the scriptPubKey instead of the one inside of ScriptProgram - val txSignatureComponentWithScriptPubKeyAdjusted = txSignatureComponent.scriptSignature match { + val sigsRemovedScript : Seq[ScriptToken] = txSignatureComponent.scriptSignature match { case s : P2SHScriptSignature => logger.info("Replacing redeemScript in txSignature component") logger.info("Redeem script: " + s.redeemScript) - TransactionSignatureComponentFactory.factory(txSignatureComponent,s.redeemScript) + logger.info("Signature: " + signature) + val sigsRemoved = removeSignaturesFromScript(s.signatures, s.redeemScript.asm) + sigsRemoved case _ : P2PKHScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature | _ : MultiSignatureScriptSignature | EmptyScriptSignature => - logger.debug("Script before sigRemoved: " + script) logger.debug("Signature: " + signature) logger.debug("PubKey: " + pubKey) - val sigRemoved = if (script.contains(ScriptConstant(signature.hex))) { - //replicates this line in bitcoin core - //https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L872 - val sigIndex = script.indexOf(ScriptConstant(signature.hex)) - logger.debug("SigIndex: " + sigIndex) - //remove sig and it's corresponding BytesToPushOntoStack - script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size) - } else script - logger.debug("Sig removed: " + sigRemoved) - val scriptPubKeySigRemoved = ScriptPubKey.fromAsm(sigRemoved) - TransactionSignatureComponentFactory.factory(txSignatureComponent,scriptPubKeySigRemoved) + val sigsRemoved = removeSignatureFromScript(signature,script) + sigsRemoved } val hashTypeByte = if (signature.bytes.size > 0) signature.bytes.last else 0x00.toByte val hashType = HashTypeFactory.fromByte(hashTypeByte) - val hashForSignature = TransactionSignatureSerializer.hashForSignature(txSignatureComponentWithScriptPubKeyAdjusted.transaction, - txSignatureComponentWithScriptPubKeyAdjusted.inputIndex, - txSignatureComponentWithScriptPubKeyAdjusted.scriptPubKey.asm, hashType) - logger.debug("Tx signature component: " + txSignatureComponentWithScriptPubKeyAdjusted) + val hashForSignature = TransactionSignatureSerializer.hashForSignature(txSignatureComponent.transaction, + txSignatureComponent.inputIndex, + sigsRemovedScript, hashType) logger.info("Hash for signature: " + BitcoinSUtil.encodeHex(hashForSignature)) val isValid = pubKey.verify(hashForSignature,signature) if (isValid) SignatureValidationSuccess else SignatureValidationFailureIncorrectSignatures @@ -143,6 +134,43 @@ trait TransactionSignatureChecker extends BitcoinSLogger { } else SignatureValidationFailureIncorrectSignatures } + + /** + * Removes the given digtial signature from the list of script tokens if it exists + * @param signature + * @param script + * @return + */ + def removeSignatureFromScript(signature : ECDigitalSignature, script : Seq[ScriptToken]) : Seq[ScriptToken] = { + if (script.contains(ScriptConstant(signature.hex))) { + //replicates this line in bitcoin core + //https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L872 + val sigIndex = script.indexOf(ScriptConstant(signature.hex)) + logger.debug("SigIndex: " + sigIndex) + //remove sig and it's corresponding BytesToPushOntoStack + script.slice(0,sigIndex-1) ++ script.slice(sigIndex+1,script.size) + } else script + } + + /** + * Removes the list of digital signatures from the list of script tokens + * @param sigs + * @param script + * @return + */ + def removeSignaturesFromScript(sigs : Seq[ECDigitalSignature], script : Seq[ScriptToken]) : Seq[ScriptToken] = { + @tailrec + def loop(remainingSigs : Seq[ECDigitalSignature], scriptTokens : Seq[ScriptToken]) : Seq[ScriptToken] = { + remainingSigs match { + case Nil => scriptTokens + case h :: t => + val newScriptTokens = removeSignatureFromScript(h,scriptTokens) + loop(t,newScriptTokens) + } + } + loop(sigs,script) + } + } object TransactionSignatureChecker extends TransactionSignatureChecker diff --git a/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala b/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala index 34a1a3527e..2c521d31ac 100644 --- a/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala +++ b/src/main/scala/org/bitcoins/crypto/TransactionSignatureSerializer.scala @@ -28,17 +28,6 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit */ private def errorHash : Seq[Byte] = BitcoinSUtil.decodeHex("0100000000000000000000000000000000000000000000000000000000000000") - /** - * Serialized the passed in script code, skipping OP_CODESEPARATORs - * definition for CScript https://github.com/bitcoin/bitcoin/blob/93c85d458ac3e2c496c1a053e1f5925f55e29100/src/script/script.h#L373 - * @param script - * @return - */ -/* - def serializeScriptCode(script : ScriptPubKey) : ScriptPubKey = script.filterNot(_ == OP_CODESEPARATOR) - -*/ - /** * Serializes a transaction to be signed by an ECKey * follows the bitcoinj implementation which can be found here diff --git a/src/main/scala/org/bitcoins/marshallers/script/ScriptParser.scala b/src/main/scala/org/bitcoins/marshallers/script/ScriptParser.scala index 5c59b4a803..0b579dfe9b 100644 --- a/src/main/scala/org/bitcoins/marshallers/script/ScriptParser.scala +++ b/src/main/scala/org/bitcoins/marshallers/script/ScriptParser.scala @@ -148,7 +148,6 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger { * @return */ private def parse(bytes : List[Byte]) : List[ScriptToken] = { - logger.debug("Parsing byte list: " + bytes + " into a list of script tokens") @tailrec def loop(bytes : List[Byte], accum : List[ScriptToken]) : List[ScriptToken] = { //logger.debug("Byte to be parsed: " + bytes.headOption) @@ -166,9 +165,6 @@ trait ScriptParser extends Factory[List[ScriptToken]] with BitcoinSLogger { private def parse(bytes : Seq[Byte]) : List[ScriptToken] = parse(bytes.toList) - - - /** * Parses a redeem script from the given script token * @param scriptToken diff --git a/src/test/scala/org/bitcoins/crypto/TransactionSignatureCheckerTest.scala b/src/test/scala/org/bitcoins/crypto/TransactionSignatureCheckerTest.scala index 37b2f83ec0..1cc083662b 100644 --- a/src/test/scala/org/bitcoins/crypto/TransactionSignatureCheckerTest.scala +++ b/src/test/scala/org/bitcoins/crypto/TransactionSignatureCheckerTest.scala @@ -3,6 +3,7 @@ package org.bitcoins.crypto import org.bitcoins.policy.Policy import org.bitcoins.protocol.script.ScriptSignature import org.bitcoins.protocol.transaction._ +import org.bitcoins.script.constant.ScriptConstant import org.bitcoins.script.flag.ScriptVerifyDerSig import org.bitcoins.util._ import org.scalatest.{FlatSpec, MustMatchers} @@ -12,65 +13,11 @@ import org.scalatest.{FlatSpec, MustMatchers} */ class TransactionSignatureCheckerTest extends FlatSpec with MustMatchers { -/* "TransactionSignatureChecker" must "check to see if an input spends a multisignature scriptPubKey correctly" in { - val (spendingTx,inputIndex,multiSigScriptPubKey,keys) = TransactionTestUtil.signedMultiSignatureTransaction - TransactionSignatureChecker.checkSignature(spendingTx,inputIndex,multiSigScriptPubKey,Seq(ScriptVerifyDerSig)) must be (SignatureValidationSuccess) + "TransactionSignatureChecker" must "remove the signatures from a p2sh scriptSig" in { + val p2shScriptSig = TestUtil.p2sh2Of3ScriptSig + val signatures = p2shScriptSig.signatures + val asmWithoutSigs = TransactionSignatureChecker.removeSignaturesFromScript(signatures,p2shScriptSig.asm) + val sigExists = signatures.map(sig => asmWithoutSigs.exists(_ == ScriptConstant(sig.hex))) + sigExists.exists(_ == true) must be (false) } - - it must "check to see if an input spends a p2sh scriptPubKey correctly" in { - val (spendingTx,input,inputIndex,creditingOutput) = TransactionTestUtil.p2shTransactionWithSpendingInputAndCreditingOutput - TransactionSignatureChecker.checkSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey,Policy.standardScriptVerifyFlags) must be (SignatureValidationSuccess) - } - - it must "check a 2/3 p2sh input script correctly" in { - val (spendingTx,input,inputIndex,creditingOutput) = TransactionTestUtil.p2sh2Of3TransactionWithSpendingInputAndCreditingOutput - TransactionSignatureChecker.checkSignature(spendingTx,inputIndex,creditingOutput.scriptPubKey,Policy.standardScriptVerifyFlags) must be (SignatureValidationSuccess) - - } - - it must "fail to validate a 2/3 p2sh input script if a digital signature is removed" in { - val (spendingTx,input,inputIndex,creditingOutput) = TransactionTestUtil.p2sh2Of3TransactionWithSpendingInputAndCreditingOutput - val scriptSig : ScriptSignature = spendingTx.inputs.head.scriptSignature - val newScriptSigAsm = Seq(scriptSig.asm.head) ++ scriptSig.asm.slice(3,scriptSig.asm.size) - val newScriptSigWithSignatureRemoved = ScriptSignatureFactory.fromAsm(newScriptSigAsm) - val newInput = TransactionInputFactory.factory(spendingTx.inputs(inputIndex),newScriptSigWithSignatureRemoved) - val txNewInputs = TransactionFactory.factory(EmptyTransaction,UpdateTransactionInputs(Seq(newInput))) - TransactionSignatureChecker.checkSignature(txNewInputs,inputIndex,creditingOutput.scriptPubKey,Policy.standardScriptVerifyFlags) must be (SignatureValidationFailureIncorrectSignatures) - } - - it must "fail to check a transaction when strict der encoding is required but the signature is not strict der encoded" in { - val (tx,inputIndex) = TransactionTestUtil.transactionWithNonStrictDerSignature - val signature = tx.inputs(inputIndex).scriptSignature.signatures.head - val result = TransactionSignatureChecker.checkSignature(tx,inputIndex,TestUtil.scriptPubKey, - ECFactory.publicKey(""), signature, true) - result must be (SignatureValidationFailureNotStrictDerEncoding) - } - - - it must "check a standard p2pk transaction" in { - val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(TestUtil.p2pkScriptPubKey) - val (spendingTx,inputIndex) = - TransactionTestUtil.buildSpendingTransaction(creditingTx,TestUtil.p2pkScriptSig,outputIndex) - val program = ScriptProgramFactory.factory(spendingTx,TestUtil.p2pkScriptPubKey,inputIndex,Seq()) - val result = TransactionSignatureChecker.checkSignature(program.txSignatureComponent) - result must be (SignatureValidationSuccess) - } - - it must "check a standard p2pkh transaction" in { - val (spendingTx, inputIndex, scriptPubKey) = TransactionTestUtil.p2pkhTransactionWithCreditingScriptPubKey - val program = ScriptProgramFactory.factory(spendingTx,scriptPubKey,inputIndex,Seq()) - val result = TransactionSignatureChecker.checkSignature(program.txSignatureComponent) - result must be (SignatureValidationSuccess) - } - - it must "fail checking a standard p2pkh transactin if the strict der encoding flag is set and we don't have a strict der encoded signature" in { - val (creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(TestUtil.p2pkhScriptPubKey) - val scriptPubKey = creditingTx.outputs(outputIndex).scriptPubKey - val (spendingTx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx, - TestUtil.p2pkhScriptSigNotStrictDerEncoded,outputIndex) - val program = ScriptProgramFactory.factory(spendingTx,scriptPubKey,inputIndex,Seq(ScriptVerifyDerSig)) - - val result = TransactionSignatureChecker.checkSignature(program.txSignatureComponent) - result must be (SignatureValidationFailureNotStrictDerEncoding) - }*/ } diff --git a/src/test/scala/org/bitcoins/crypto/TransactionSignatureSerializerTest.scala b/src/test/scala/org/bitcoins/crypto/TransactionSignatureSerializerTest.scala index 0e31122bf0..682a3610b8 100644 --- a/src/test/scala/org/bitcoins/crypto/TransactionSignatureSerializerTest.scala +++ b/src/test/scala/org/bitcoins/crypto/TransactionSignatureSerializerTest.scala @@ -7,7 +7,7 @@ import org.bitcoinj.core.Transaction.SigHash import org.bitcoinj.params.TestNet3Params import org.bitcoinj.script.{ScriptBuilder, ScriptChunk, ScriptOpCodes} import org.bitcoins.marshallers.script.ScriptParser -import org.bitcoins.protocol.script.{ScriptPubKey, UpdateScriptPubKeyAsm, UpdateScriptPubKeyBytes} +import org.bitcoins.protocol.script._ import org.bitcoins.protocol.transaction._ import org.bitcoins.script.ScriptOperationFactory import org.bitcoins.script.bitwise.OP_EQUALVERIFY @@ -23,7 +23,7 @@ import scala.collection.JavaConversions._ /** * Created by chris on 2/19/16. */ -class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers { +class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with BitcoinSLogger { val scriptPubKey = BitcoinjConversions.toScriptPubKey(BitcoinJTestUtil.multiSigScript) @@ -420,13 +420,12 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers { val inputIndex = 0 val spendingTx = Transaction(rawTx) val scriptPubKeyFromString = ScriptParser.fromString("0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG") - println("Script pubKey from string asm: " + scriptPubKeyFromString) val scriptPubKey = ScriptPubKey.fromAsm(scriptPubKeyFromString) - require(scriptPubKey.hex == "4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288ac") val serializedTxForSig : String = BitcoinSUtil.encodeHex( TransactionSignatureSerializer.serializeForSignature(spendingTx,inputIndex,scriptPubKey.asm,SIGHASH_ALL(1.toByte))) //serialization is from bitcoin core serializedTxForSig must be ("0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c500000000ca4cae606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e2074607576a914bfd7436b6265aa9de506f8a994f881ff08cc287288acffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac0000000001000000") + } } diff --git a/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala b/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala index 6bc85692b6..20b5d13620 100644 --- a/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala +++ b/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala @@ -49,15 +49,15 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger { //use this to represent a single test case from script_valid.json -/* val lines = + val lines = """ |[ - |[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], - |"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH"] + |[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]], + |"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"] | |] - """.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/util/TestUtil.scala b/src/test/scala/org/bitcoins/util/TestUtil.scala index bb3a1aa0cb..6c51b6f87e 100644 --- a/src/test/scala/org/bitcoins/util/TestUtil.scala +++ b/src/test/scala/org/bitcoins/util/TestUtil.scala @@ -88,7 +88,10 @@ object TestUtil { def p2shInputScriptLargeSignature = ScriptSignature(rawP2shInputScriptLargeSignature) def rawP2sh2Of3ScriptSig = "004730440220028c02f14654a0cc12c7e3229adb09d5d35bebb6ba1057e39adb1b2706607b0d0220564fab12c6da3d5acef332406027a7ff1cbba980175ffd880e1ba1bf40598f6b014830450221009362f8d67b60773745e983d07ba10efbe566127e244b724385b2ca2e47292dda022033def393954c320653843555ddbe7679b35cc1cacfe1dad923977de8cd6cc6d7014c695221025e9adcc3d65c11346c8a6069d6ebf5b51b348d1d6dc4b95e67480c34dc0bc75c21030585b3c80f4964bf0820086feda57c8e49fa1eab925db7c04c985467973df96521037753a5e3e9c4717d3f81706b38a6fb82b5fb89d29e580d7b98a37fea8cdefcad53ae" - def p2sh2Of3ScriptSig = ScriptSignature(rawP2sh2Of3ScriptSig) + def p2sh2Of3ScriptSig : P2SHScriptSignature = ScriptSignature(rawP2sh2Of3ScriptSig) match { + case p2sh : P2SHScriptSignature => p2sh + case _ : ScriptSignature => throw new RuntimeException + } //txid on testnet 44e504f5b7649d215be05ad9f09026dee95201244a3b218013c504a6a49a26ff //this tx has multiple inputs and outputs