mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 21:34:39 +01:00
Fixing bug in OP_CHECKMULTISIG where the correct number of pubkeys weren't being pushed onto the stack
This commit is contained in:
parent
0ff2022c6d
commit
2e9965c5ba
@ -119,12 +119,14 @@ trait ControlOperationsInterpreter {
|
||||
* @return
|
||||
*/
|
||||
def opVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
//TODO: There is a bug here, if the value is cast to an int and is != 0 then we need to pop the cast values
|
||||
//off of the stack..
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY")
|
||||
require(program.stack.size > 0, "Stack must have at least one element on it to run OP_VERIFY")
|
||||
if (program.stack.head != OP_0 && program.stack.head != ScriptFalse ) {
|
||||
ScriptProgramFactory.factory(program, program.stack,program.script.tail,true)
|
||||
ScriptProgramFactory.factory(program, program.stack.tail,program.script.tail,true)
|
||||
} else if (program.stack.exists(t => t != OP_0 && t != ScriptFalse)) {
|
||||
ScriptProgramFactory.factory(program, program.stack,program.script.tail,true)
|
||||
ScriptProgramFactory.factory(program, program.stack.tail,program.script.tail,true)
|
||||
} else ScriptProgramFactory.factory(program, program.stack,program.script.tail, false)
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,19 @@ package org.scalacoin.script.crypto
|
||||
|
||||
import org.scalacoin.protocol.script.ScriptPubKey
|
||||
import org.scalacoin.protocol.transaction.Transaction
|
||||
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
|
||||
import org.scalacoin.script.{ScriptProgramFactory, ScriptProgramImpl, ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.{CryptoUtil, ScalacoinUtil}
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
||||
/**
|
||||
* Created by chris on 1/6/16.
|
||||
*/
|
||||
trait CryptoInterpreter extends ScalacoinUtil {
|
||||
trait CryptoInterpreter extends ControlOperationsInterpreter with ScalacoinUtil {
|
||||
|
||||
private def logger = LoggerFactory.getLogger(this.getClass())
|
||||
/**
|
||||
* The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
|
||||
* @param program
|
||||
@ -159,23 +162,53 @@ trait CryptoInterpreter extends ScalacoinUtil {
|
||||
* @return
|
||||
*/
|
||||
def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
|
||||
|
||||
val signatures = program.transaction.inputs.head.scriptSignature
|
||||
|
||||
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")
|
||||
//head should be n for m/n
|
||||
val n : Int = program.stack.head match {
|
||||
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")
|
||||
}
|
||||
|
||||
val m : Int = program.stack(n.toInt+1) match {
|
||||
logger.debug("nPossibleSignatures: " + nPossibleSignatures)
|
||||
|
||||
val pubKeys : Seq[ScriptToken] = program.stack.tail.slice(0,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")
|
||||
}
|
||||
|
||||
if (m == 0) ScriptProgramFactory.factory(program, program.stack.slice(m + n + 3,
|
||||
program.stack.size), program.script.tail,true)
|
||||
logger.debug("mRequiredSignatures: " + mRequiredSignatures )
|
||||
val signatures : Seq[ScriptToken] = program.stack.slice(nPossibleSignatures+2,mRequiredSignatures+2)
|
||||
|
||||
//+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
|
||||
//if there are zero signatures required for the m/n signature
|
||||
//the transaction is valid by default
|
||||
if (mRequiredSignatures == 0) ScriptProgramFactory.factory(program, ScriptTrue :: restOfStack, program.script.tail,true)
|
||||
else program
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs OP_CHECKMULTISIG with an OP_VERIFY afterwards
|
||||
* @param program
|
||||
* @return
|
||||
*/
|
||||
def opCheckMultiSigVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIGVERIFY, "Script top must be OP_CHECKMULTISIGVERIFY")
|
||||
require(program.stack.size > 2, "Stack must contain at least 3 items for OP_CHECKMULTISIGVERIFY")
|
||||
val newScript = OP_CHECKMULTISIG :: OP_VERIFY :: program.script.tail
|
||||
val newProgram = ScriptProgramFactory.factory(program,newScript, ScriptProgramFactory.Script)
|
||||
val programFromOpCheckMultiSig = opCheckMultiSig(newProgram)
|
||||
val programFromOpVerify = opVerify(programFromOpCheckMultiSig)
|
||||
programFromOpVerify
|
||||
}
|
||||
|
||||
private def hashForSignature(inputScript : Seq[ScriptToken], spendingTx : Transaction,
|
||||
inputIndex : Int, hashType : HashType) : String = {
|
||||
require(inputIndex < spendingTx.inputs.size, "Given input index is out of range of the inputs in the spending tx")
|
||||
|
@ -128,6 +128,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
||||
case OP_SHA256 :: t => loop(opSha256(program))
|
||||
case OP_HASH256 :: t => loop(opHash256(program))
|
||||
case OP_CODESEPARATOR :: t => loop(opCodeSeparator(program))
|
||||
case OP_CHECKMULTISIG :: t => loop(opCheckMultiSig(program))
|
||||
case OP_CHECKMULTISIGVERIFY :: t => loop(opCheckMultiSigVerify(program))
|
||||
//reserved operations
|
||||
case (nop : NOP) :: t => loop(ScriptProgramFactory.factory(program,program.stack,t))
|
||||
|
||||
|
@ -18,12 +18,14 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
||||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val result = opVerify(program)
|
||||
result.stack.isEmpty must be (true)
|
||||
result.script.isEmpty must be (true)
|
||||
result.valid must be (true)
|
||||
}
|
||||
|
||||
it must "have OP_VERIFY evaluate to true when there are multiple items on the stack that can be cast to an int" in {
|
||||
//for this test case in bitcoin core's script test suite
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_valid.json#L77
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_valid.json#L21
|
||||
val stack = List(OP_0, OP_0, OP_0, OP_0, ScriptNumberImpl(1), ScriptNumberImpl(1))
|
||||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
|
@ -63,13 +63,13 @@ 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 =
|
||||
"""
|
||||
|
|
||||
|[["127", "0x01 0x7F EQUAL", "P2SH,STRICTENC"]]
|
||||
""".stripMargin*/
|
||||
|[["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "Zero sigs means no sigs are checked"]]
|
||||
""".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
|
||||
|
Loading…
Reference in New Issue
Block a user