Fixing bug of finding hash type of the empty digital signature

This commit is contained in:
Chris Stewart 2016-03-26 14:34:02 -05:00
parent 932705595b
commit 46eb3eb7ea
6 changed files with 38 additions and 22 deletions

View file

@ -122,10 +122,13 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
scriptPubKey match {
case x : P2SHScriptPubKey =>
val redeemScript = p2shScriptSignature.redeemScript
redeemScript match {
case y : MultiSignatureScriptPubKey =>
//the signatures & pubkeys need to be reversed so that they are evaluated the
//same way as if they were getting pushed then popped off of a stack
multiSignatureHelper(spendingTransaction, inputIndex, y,
p2shScriptSignature.signatures.toList, p2shScriptSignature.publicKeys.toList, requireStrictDEREncoding, y.requiredSigs)
p2shScriptSignature.signatures.toList.reverse, p2shScriptSignature.publicKeys.toList.reverse, requireStrictDEREncoding, y.requiredSigs)
case _ : P2PKHScriptPubKey | _ : P2PKScriptPubKey | _ : P2SHScriptPubKey | _ : NonStandardScriptPubKey | EmptyScriptPubKey =>
throw new RuntimeException("Don't know how to implement this scriptPubKeys in a redeemScript")
}
@ -160,9 +163,12 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
multiSignatureScript : MultiSignatureScriptSignature, requireStrictDEREncoding : Boolean) : Boolean = {
scriptPubKey match {
case x : MultiSignatureScriptPubKey =>
//the signatures & pubkeys need to be reversed so that they are evaluated the
//same way as if they were getting pushed then popped off of a stack
logger.info("multisig public keys: " + x.publicKeys)
multiSignatureHelper(spendingTransaction,inputIndex,x,multiSignatureScript.signatures.toList,
x.publicKeys.toList,requireStrictDEREncoding,x.requiredSigs)
logger.info("multisig sigs: " + multiSignatureScript.signatures)
multiSignatureHelper(spendingTransaction,inputIndex,x,multiSignatureScript.signatures.toList.reverse,
x.publicKeys.toList.reverse,requireStrictDEREncoding,x.requiredSigs)
case x : P2PKHScriptPubKey =>
logger.warn("Trying to check if a multisignature scriptSig spends a p2pkh scriptPubKey properly - this is trivially false")
false
@ -218,6 +224,7 @@ trait TransactionSignatureChecker extends BitcoinSLogger {
private def multiSignatureHelper(spendingTransaction : Transaction, inputIndex : Int, scriptPubKey : MultiSignatureScriptPubKey,
sigs : List[ECDigitalSignature], pubKeys : List[ECPublicKey], requireStrictDEREncoding : Boolean,
requiredSigs : Long) : Boolean = {
logger.info("Signatures inside of helper: " + sigs)
logger.info("public keys inside of helper: " + pubKeys)
if (sigs.size > pubKeys.size) {
//this is how bitcoin core treats this. If there are ever any more

View file

@ -64,12 +64,8 @@ trait MultiSignatureScriptPubKey extends ScriptPubKey {
def requiredSigs = {
val asmWithoutPushOps = asm.filterNot(_.isInstanceOf[BytesToPushOntoStack])
val opCheckMultiSigIndex = if (asm.indexOf(OP_CHECKMULTISIG) != -1) asmWithoutPushOps.indexOf(OP_CHECKMULTISIG) else asmWithoutPushOps.indexOf(OP_CHECKMULTISIGVERIFY)
logger.debug("OP_CHECKMULTISIG index: " + opCheckMultiSigIndex)
logger.debug("Max sigs: " + maxSigs)
logger.debug("asm filter push ops: " + asmWithoutPushOps)
//magic number 2 represents the maxSig operation and the OP_CHECKMULTISIG operation at the end of the asm
val numSigsRequired = asmWithoutPushOps(opCheckMultiSigIndex - maxSigs.toInt - 2)
logger.debug("num sigs required: " + numSigsRequired)
numSigsRequired match {
case x : ScriptNumber => x.num
case _ => throw new RuntimeException("The first element of the multisignature pubkey must be a script number operation\n" +

View file

@ -1,11 +1,11 @@
package org.scalacoin.protocol.script
import org.scalacoin.crypto.{ECPublicKey, ECFactory, ECDigitalSignature}
import org.scalacoin.crypto.{EmptyDigitalSignature, ECPublicKey, ECFactory, ECDigitalSignature}
import org.scalacoin.marshallers.script.{RawScriptSignatureParser, RawScriptPubKeyParser, ScriptParser}
import org.scalacoin.marshallers.transaction.TransactionElement
import org.scalacoin.script.constant._
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, HashType, HashTypeFactory}
import org.scalacoin.script.crypto.{SIGHASH_ALL, OP_CHECKMULTISIG, HashType, HashTypeFactory}
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
import org.slf4j.LoggerFactory
@ -39,9 +39,10 @@ sealed trait ScriptSignature extends TransactionElement with ScriptSignatureFact
* @return
*/
def hashType(digitalSignature: ECDigitalSignature) = {
require(HashTypeFactory.fromByte(digitalSignature.bytes.last).isDefined,
"Hash type could not be read for this scriptSig: " + digitalSignature.hex)
HashTypeFactory.fromByte(digitalSignature.bytes.last).get
digitalSignature match {
case EmptyDigitalSignature => SIGHASH_ALL
case sig : ECDigitalSignature => HashTypeFactory.fromByte(digitalSignature.bytes.last).get
}
}
/**
@ -156,8 +157,8 @@ trait MultiSignatureScriptSignature extends ScriptSignature {
* @return
*/
def signatures : Seq[ECDigitalSignature] = {
asm.filter(_.isInstanceOf[ScriptConstant])
.filterNot(_.isInstanceOf[ScriptNumberOperation])
asm.tail.filter(_.isInstanceOf[ScriptConstant])
/* .filterNot(_.isInstanceOf[BytesToPushOntoStack])*/
.map(sig => ECFactory.digitalSignature(sig.hex))
}
}

View file

@ -58,4 +58,6 @@ class MultiSignatureScriptPubKeyTest extends FlatSpec with MustMatchers {
))
}
}

View file

@ -1,5 +1,7 @@
package org.scalacoin.protocol.script
import org.scalacoin.crypto.{EmptyDigitalSignature, ECFactory}
import org.scalacoin.script.constant.{ScriptConstantImpl, BytesToPushOntoStackImpl, OP_0}
import org.scalacoin.util.TransactionTestUtil
import org.scalatest.{MustMatchers, FlatSpec}
@ -13,4 +15,12 @@ class MultiSignatureScriptSignatureTest extends FlatSpec with MustMatchers {
val scriptSig = spendingTx.inputs(inputIndex).scriptSignature
scriptSig.signatures.size must be (2)
}
it must "give us the empty signature back when it is encoded as an OP_0 (this pushes an empty signature onto the stack)" in {
val multiSigScriptSignature = ScriptSignatureFactory.fromAsm(List(OP_0, BytesToPushOntoStackImpl(71),
ScriptConstantImpl("30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201"), OP_0))
multiSigScriptSignature.signatures must be (Seq(
ECFactory.digitalSignature("30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201"),
EmptyDigitalSignature))
}
}

View file

@ -85,18 +85,18 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
val source = scala.io.Source.fromFile("src/test/scala/org/scalacoin/script/interpreter/script_valid.json")
//use this to represent a single test case from script_valid.json
/* val lines =
val lines =
"""
|
|[[
"0 0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501",
"2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT",
"STRICTENC",
"2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but second signature invalid. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid signature."
]]
""".stripMargin*/
| "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0",
| "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT",
| "DERSIG",
| "BIP66 example 12, with DERSIG"
|]]
""".stripMargin
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
//val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val json = lines.parseJson
val testCasesOpt : Seq[Option[CoreTestCase]] = json.convertTo[Seq[Option[CoreTestCase]]]
val testCases : Seq[CoreTestCase] = testCasesOpt.flatten