Adding code to implement the script flag NULLDUMMy, changing how mulsignature script sigs are assigned in the factory. Now it can be ANY script number operation. When the script is evaluated it checks the NULLDUMMY flag and if the scriptsig starts with an OP_0

This commit is contained in:
Chris Stewart 2016-03-28 09:22:52 -05:00
parent 2a1f8742ce
commit ff0b117c0e
3 changed files with 49 additions and 44 deletions

View file

@ -158,7 +158,6 @@ trait MultiSignatureScriptSignature extends ScriptSignature {
*/
def signatures : Seq[ECDigitalSignature] = {
asm.tail.filter(_.isInstanceOf[ScriptConstant])
/* .filterNot(_.isInstanceOf[BytesToPushOntoStack])*/
.map(sig => ECFactory.digitalSignature(sig.hex))
}
}

View file

@ -4,7 +4,7 @@ import org.scalacoin.crypto._
import org.scalacoin.protocol.script._
import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.scalacoin.script.flag.ScriptVerifyDerSig
import org.scalacoin.script.flag.{ScriptVerifyNullDummy, ScriptVerifyDerSig}
import org.scalacoin.script.{ScriptProgramFactory, ScriptProgramImpl, ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
@ -170,46 +170,52 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIG, "Script top must be OP_CHECKMULTISIG")
require(program.stack.size > 2, "Stack must contain at least 3 items for OP_CHECKMULTISIG")
val isValidSignatures = TransactionSignatureChecker.checkSignature(program)
if (program.flags.contains(ScriptVerifyNullDummy) && program.scriptSignature.asm.head != OP_0) {
logger.warn("Script flag null dummy was set however the first element in the script signature was not an OP_0")
ScriptProgramFactory.factory(program,false)
} else {
val isValidSignatures = TransactionSignatureChecker.checkSignature(program)
//these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : Int = program.stack.head match {
case s : ScriptNumber => s.num.toInt
case _ => throw new RuntimeException("n must be a script number for OP_CHECKMULTISIG")
}
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
val stackWithoutPubKeys = program.stack.tail.slice(nPossibleSignatures,program.stack.tail.size)
val mRequiredSignatures : Int = stackWithoutPubKeys.head match {
case s: ScriptNumber => s.num.toInt
case _ => throw new RuntimeException("m must be a script number for OP_CHECKMULTISIG")
}
logger.debug("mRequiredSignatures: " + mRequiredSignatures )
//these next lines remove the appropriate stack/script values after the signatures have been checked
val nPossibleSignatures : Int = program.stack.head match {
case s : ScriptNumber => s.num.toInt
case _ => throw new RuntimeException("n must be a script number for OP_CHECKMULTISIG")
}
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
val stackWithoutPubKeys = program.stack.tail.slice(nPossibleSignatures,program.stack.tail.size)
val mRequiredSignatures : Int = stackWithoutPubKeys.head match {
case s: ScriptNumber => s.num.toInt
case _ => throw new RuntimeException("m must be a script number for OP_CHECKMULTISIG")
}
logger.debug("mRequiredSignatures: " + mRequiredSignatures )
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
val signaturesScriptTokens : Seq[ScriptToken] = program.stack.tail.slice(nPossibleSignatures + 1, nPossibleSignatures + mRequiredSignatures + 1)
val signatures = signaturesScriptTokens.map(token => ECFactory.digitalSignature(token.bytes))
logger.debug("Signatures on the stack: " + signatures)
//+1 is for bug in OP_CHECKMULTSIG that requires an extra OP to be pushed onto the stack
val stackWithoutPubKeysAndSignatures = stackWithoutPubKeys.tail.slice(mRequiredSignatures+1, stackWithoutPubKeys.tail.size)
val restOfStack = stackWithoutPubKeysAndSignatures
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
val signaturesScriptTokens : Seq[ScriptToken] = program.stack.tail.slice(nPossibleSignatures + 1, nPossibleSignatures + mRequiredSignatures + 1)
val signatures = signaturesScriptTokens.map(token => ECFactory.digitalSignature(token.bytes))
logger.debug("Signatures on the stack: " + signatures)
//+1 is for bug in OP_CHECKMULTSIG that requires an extra OP to be pushed onto the stack
val stackWithoutPubKeysAndSignatures = stackWithoutPubKeys.tail.slice(mRequiredSignatures+1, stackWithoutPubKeys.tail.size)
val restOfStack = stackWithoutPubKeysAndSignatures
isValidSignatures match {
case SignatureValidationSuccess =>
//means that all of the signatures were correctly encoded and
//that all of the signatures were valid signatures for the given
//public keys
ScriptProgramFactory.factory(program, ScriptTrue :: restOfStack, program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
//this means the script fails immediately
//set the valid flag to false on the script
//see BIP66 for more information on this
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
ScriptProgramFactory.factory(program, restOfStack, program.script.tail,false)
case SignatureValidationfailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgramFactory.factory(program, ScriptFalse :: restOfStack, program.script.tail)
isValidSignatures match {
case SignatureValidationSuccess =>
//means that all of the signatures were correctly encoded and
//that all of the signatures were valid signatures for the given
//public keys
ScriptProgramFactory.factory(program, ScriptTrue :: restOfStack, program.script.tail)
case SignatureValidationFailureNotStrictDerEncoding =>
//this means the script fails immediately
//set the valid flag to false on the script
//see BIP66 for more information on this
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#specification
ScriptProgramFactory.factory(program, restOfStack, program.script.tail,false)
case SignatureValidationfailureIncorrectSignatures =>
//this means that signature verification failed, however all signatures were encoded correctly
//just push a ScriptFalse onto the stack
ScriptProgramFactory.factory(program, ScriptFalse :: restOfStack, program.script.tail)
}
}
}

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 =
"""
|
|[[
"0x47 0x304402206177d513ec2cda444c021a1f4f656fc4c72ba108ae063e157eb86dc3575784940220666fc66702815d0e5413bb9b1df22aed44f5f1efb8b99d41dd5dc9a5be6d205205",
"0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG",
"1 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901",
"3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG",
"",
"P2PK with undefined hashtype but no STRICTENC"
"3-of-3 with nonzero dummy but no NULLDUMMY"
]]
""".stripMargin
""".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