mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 23:08:31 +01:00
Fixing various bugs in Arithmetic interpreter - also fixing bug in ScriptInterpreter for detecting if the return value is false
This commit is contained in:
parent
fc71293256
commit
8dadfad1aa
6 changed files with 59 additions and 47 deletions
|
@ -2,7 +2,7 @@ package org.scalacoin.script
|
|||
|
||||
import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
|
||||
import org.scalacoin.protocol.transaction.Transaction
|
||||
import org.scalacoin.script.constant.{OP_0, ScriptNumberImpl, ScriptFalse, ScriptToken}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.flag.ScriptFlag
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ trait ScriptProgram {
|
|||
*/
|
||||
def stackTopIsFalse : Boolean = {
|
||||
if (stack.headOption.isDefined &&
|
||||
(stack.head == ScriptFalse || stack.head == ScriptNumberImpl(0) || stack.head == OP_0)) true
|
||||
(stack.head == ScriptFalse || stack.head == ScriptNumberImpl(0) || stack.head == OP_0 || stack.head == OP_FALSE)) true
|
||||
else if (!stack.headOption.isDefined) true
|
||||
else false
|
||||
}
|
||||
|
|
|
@ -118,11 +118,12 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
require(program.script.headOption.isDefined && program.script.head == OP_NOT, "Script top must be OP_NOT")
|
||||
require(program.stack.size > 0, "Stack size must be 1 or more perform an OP_NOT")
|
||||
val newStackTop = program.stack.head match {
|
||||
case OP_0 => OP_1
|
||||
case OP_0 => OP_TRUE
|
||||
case OP_FALSE => OP_TRUE
|
||||
case OP_1 => OP_0
|
||||
case ScriptNumberImpl(0) => OP_1
|
||||
case ScriptNumberImpl(1) => OP_0
|
||||
case _ => OP_0
|
||||
case ScriptNumberImpl(0) => OP_TRUE
|
||||
case ScriptNumberImpl(1) => OP_FALSE
|
||||
case _ => OP_FALSE
|
||||
}
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail)
|
||||
}
|
||||
|
@ -154,7 +155,9 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_BOOLAND")
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
val newStackTop = if ( b == a && (a == ScriptNumberImpl(0) || a == OP_0)) OP_1 else OP_0
|
||||
val aIsFalse = (a == ScriptNumberImpl(0) || a == OP_0)
|
||||
val bIsFalse = (b == ScriptNumberImpl(0) || b == OP_0)
|
||||
val newStackTop = if (aIsFalse || bIsFalse) OP_FALSE else OP_TRUE
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail)
|
||||
|
||||
}
|
||||
|
@ -184,12 +187,13 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
val isSame = (a,b) match {
|
||||
case (x : ScriptNumberOperation, y : ScriptNumberOperation) => x == y
|
||||
case (x : ScriptNumberOperation, y : ScriptNumber) => x.scriptNumber == y
|
||||
case (x : ScriptNumber, y : ScriptNumberOperation) => x == y.scriptNumber
|
||||
case (x,y) => x == y
|
||||
}
|
||||
|
||||
val newStackTop = if (isSame) OP_1 else OP_0
|
||||
val newStackTop = if (isSame) OP_TRUE else OP_FALSE
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
|
@ -386,14 +390,11 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
val a = program.stack.tail.tail.head
|
||||
|
||||
val isWithinRange = (a,b,c) match {
|
||||
case (x : ScriptNumberOperation, y : ScriptNumber, z : ScriptNumber) => z >= x && z < y
|
||||
case (x : ScriptNumber, y : ScriptNumberOperation, z : ScriptNumber) => z >= x && z < y.scriptNumber
|
||||
case (x : ScriptNumber, y : ScriptNumber, z : ScriptNumberOperation) => z.scriptNumber >= x && z.scriptNumber < y
|
||||
case (x : ScriptNumber, y : ScriptNumber, z : ScriptNumber) => z >= x && z < y
|
||||
case (x,y,z) => z.toLong >= x.toLong && z.toLong < y.toLong
|
||||
case (x : ScriptNumber, y : ScriptNumber, z : ScriptNumber) => x >= y && x < z
|
||||
case (x,y,z) => x.toLong >= y.toLong && x.toLong < z.toLong
|
||||
}
|
||||
|
||||
val newStackTop = if (isWithinRange) OP_1 else OP_0
|
||||
val newStackTop = if (isWithinRange) OP_TRUE else OP_FALSE
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.scalacoin.script.constant
|
||||
|
||||
import org.scalacoin.script.{ScriptProgramFactory, ScriptProgramImpl, ScriptProgram}
|
||||
import org.scalacoin.util.BitcoinSUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
@ -108,7 +109,13 @@ trait ConstantInterpreter {
|
|||
}
|
||||
|
||||
val (newScript,bytesToPushOntoStack) = takeUntilBytesNeeded(program.script.tail,List())
|
||||
ScriptProgramFactory.factory(program, bytesToPushOntoStack ++ program.stack, newScript)
|
||||
logger.debug("new script: " + newScript)
|
||||
logger.debug("Bytes to push onto stack" + bytesToPushOntoStack)
|
||||
val constant : ScriptToken = if (bytesToPushOntoStack.size == 1) bytesToPushOntoStack.head
|
||||
else ScriptConstantFactory.fromHex(BitcoinSUtil.flipEndianess(bytesToPushOntoStack.flatMap(_.bytes)))
|
||||
|
||||
logger.debug("Constant: " + constant)
|
||||
ScriptProgramFactory.factory(program, constant :: program.stack, newScript)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
//in this case, just remove OP_CLTV from the stack and continue
|
||||
else loop(ScriptProgramFactory.factory(program, program.script.tail, ScriptProgramFactory.Script))
|
||||
//no more script operations to run, True is represented by any representation of non-zero
|
||||
case Nil => (program.stack.headOption != Some(ScriptFalse), program)
|
||||
case Nil => (program.stackTopIsTrue, program)
|
||||
|
||||
case h :: t => throw new RuntimeException(h + " was unmatched")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.scalacoin.script.arithmetic
|
||||
|
||||
import org.scalacoin.script.{ScriptProgramFactory, ScriptProgramImpl}
|
||||
import org.scalacoin.script.constant.{OP_0, OP_1, ScriptConstantImpl, ScriptNumberImpl}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
|
@ -219,7 +219,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNot(program)
|
||||
|
||||
newProgram.stack.head must be (OP_1)
|
||||
newProgram.stackTopIsTrue must be (true)
|
||||
newProgram.stack.head must be (OP_TRUE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
@ -229,7 +230,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNot(program)
|
||||
|
||||
newProgram.stack.head must be (OP_0)
|
||||
newProgram.stackTopIsFalse must be (true)
|
||||
newProgram.stack.head must be (OP_FALSE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
@ -259,7 +261,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opBoolAnd(program)
|
||||
|
||||
newProgram.stack.head must be (OP_1)
|
||||
newProgram.stackTopIsFalse must be (true)
|
||||
newProgram.stack.head must be (OP_FALSE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
|
||||
val stack1 = List(OP_0, OP_0)
|
||||
|
@ -267,7 +270,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program1 = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram1 = opBoolAnd(program)
|
||||
|
||||
newProgram1.stack.head must be (OP_1)
|
||||
newProgram.stackTopIsFalse must be (true)
|
||||
newProgram1.stack.head must be (OP_FALSE)
|
||||
newProgram1.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
@ -277,7 +281,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opBoolAnd(program)
|
||||
|
||||
newProgram.stack.head must be (OP_0)
|
||||
newProgram.stackTopIsTrue must be (false)
|
||||
newProgram.stack.head must be (OP_FALSE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
@ -287,7 +292,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opBoolAnd(program)
|
||||
|
||||
newProgram.stack.head must be (OP_0)
|
||||
newProgram.stackTopIsTrue must be (true)
|
||||
newProgram.stack.head must be (OP_TRUE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
@ -327,7 +333,18 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNumEqual(program)
|
||||
|
||||
newProgram.stack.head must be (OP_1)
|
||||
newProgram.stack.head must be (OP_TRUE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
it must "evaulate an OP_NUMEQUAL for two OP_0" in {
|
||||
val stack = List(OP_0, OP_0)
|
||||
val script = List(OP_NUMEQUAL)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNumEqual(program)
|
||||
|
||||
newProgram.stackTopIsTrue must be (true)
|
||||
newProgram.stack.head must be (OP_TRUE)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
@ -341,6 +358,8 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
it must "evaluate an OP_NUMNOTEQUAL for two numbers that are not the same" in {
|
||||
val stack = List(OP_0, ScriptNumberImpl(1))
|
||||
val script = List(OP_NUMNOTEQUAL)
|
||||
|
@ -424,19 +443,19 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
}
|
||||
|
||||
it must "evaluate an OP_WITHIN correctly" in {
|
||||
val stack = List(OP_0,ScriptNumberImpl(2), ScriptNumberImpl(1))
|
||||
val stack = List(ScriptNumberImpl(2), ScriptNumberImpl(1), OP_0)
|
||||
val script = List(OP_WITHIN)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opWithin(program)
|
||||
newProgram.stack must be (List(OP_0))
|
||||
newProgram.stack must be (List(OP_FALSE))
|
||||
newProgram.script.isEmpty must be (true)
|
||||
|
||||
|
||||
val stack1 = List(OP_0, ScriptNumberImpl(1),ScriptNumberImpl(0))
|
||||
val stack1 = List(ScriptNumberImpl(1), OP_0, ScriptNumberImpl(0))
|
||||
val script1 = List(OP_WITHIN)
|
||||
val program1 = ScriptProgramFactory.factory(TestUtil.testProgram, stack1,script1)
|
||||
val newProgram1 = opWithin(program1)
|
||||
newProgram1.stack must be (List(OP_1))
|
||||
newProgram1.stack must be (List(OP_TRUE))
|
||||
newProgram1.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
|
|
|
@ -53,17 +53,8 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|
|||
run(program) must equal (true)
|
||||
}
|
||||
|
||||
it must "evaluate a NOP correctly" in {
|
||||
val stack = List()
|
||||
val script = List(OP_NOP)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram,stack,script)
|
||||
run(program) must equal (true)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
it must "evaluate all valid scripts from the bitcoin core script_valid.json" in {
|
||||
import CoreTestCaseProtocol._
|
||||
|
||||
|
@ -106,9 +97,9 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|
|||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
it must "evaluate all valid scripts from the bitcoin core script_invalid.json" in {
|
||||
/* it must "evaluate all valid scripts from the bitcoin core script_invalid.json" in {
|
||||
import CoreTestCaseProtocol._
|
||||
|
||||
/**
|
||||
|
@ -129,21 +120,15 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|
|||
|
||||
|
||||
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
|
||||
logger.info("1")
|
||||
val json = lines.parseJson
|
||||
logger.info("2")
|
||||
val testCasesOpt : Seq[Option[CoreTestCase]] = json.convertTo[Seq[Option[CoreTestCase]]]
|
||||
logger.info("3")
|
||||
val testCases : Seq[CoreTestCase] = testCasesOpt.flatten
|
||||
logger.info("4")
|
||||
|
||||
|
||||
for {
|
||||
testCase <- testCases
|
||||
_ = logger.info(testCase.toString)
|
||||
(creditingTx,outputIndex) = TransactionTestUtil.buildCreditingTransaction(testCase.scriptPubKey.scriptPubKey)
|
||||
_ = logger.info("6")
|
||||
(tx,inputIndex) = TransactionTestUtil.buildSpendingTransaction(creditingTx,testCase.scriptSig.scriptSignature,outputIndex)
|
||||
_ = logger.info("7")
|
||||
} yield {
|
||||
|
||||
require(testCase.scriptPubKey.asm == testCase.scriptPubKey.scriptPubKey.asm)
|
||||
|
@ -162,5 +147,5 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue