diff --git a/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala b/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala index 5d50683208..9ad235d575 100644 --- a/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala @@ -335,6 +335,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s) ScriptProgramFactory.factory(program,false) } + case s : ScriptConstant => + val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex)) + val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack) + performUnaryArithmeticOperation(newProgram, op) case s : ScriptToken => logger.error("Stack top must be a script number to perform an arithmetic operation") ScriptProgramFactory.factory(program,false) @@ -364,9 +368,16 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack) performBinaryArithmeticOperation(newProgram, op) case (x : ScriptNumber, y : ScriptConstant) => + //interpret y as a number val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex)) val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack) performBinaryArithmeticOperation(newProgram, op) + case (x : ScriptConstant, y : ScriptConstant) => + //interpret x and y as a number + val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex)) + val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex)) + val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack) + performBinaryArithmeticOperation(newProgram, op) case (x : ScriptToken, y : ScriptToken) => logger.error("The top two stack items must be script numbers to perform an arithmetic operation") ScriptProgramFactory.factory(program,false) @@ -390,7 +401,22 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { logger.error("Cannot perform boolean operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y) ScriptProgramFactory.factory(program,false) } - + case (x : ScriptConstant, y : ScriptNumber) => + //interpret x as a number + val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex)) + val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack) + performBinaryBooleanOperation(newProgram, op) + case (x : ScriptNumber, y : ScriptConstant) => + //interpret y as a number + val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex)) + val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack) + performBinaryBooleanOperation(newProgram, op) + case (x : ScriptConstant, y : ScriptConstant) => + //interpret x and y as a number + val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex)) + val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex)) + val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack) + performBinaryBooleanOperation(newProgram, op) case (x : ScriptToken, y : ScriptToken) => logger.error("The top two stack items must be script numbers to perform an arithmetic operation") ScriptProgramFactory.factory(program,false) diff --git a/src/main/scala/org/scalacoin/script/constant/Constants.scala b/src/main/scala/org/scalacoin/script/constant/Constants.scala index 749dbb1f7d..d54bd690b6 100644 --- a/src/main/scala/org/scalacoin/script/constant/Constants.scala +++ b/src/main/scala/org/scalacoin/script/constant/Constants.scala @@ -6,21 +6,53 @@ import org.scalacoin.util.{BitcoinSUtil} * Created by chris on 1/6/16. */ - +/** + * This is the root class of Script. Every element in the Script language is a + * ScriptToken - think of this the same way you think about Object in Java. + */ sealed trait ScriptToken { + /** + * The hexadecimal representation of this script token + * @return + */ def hex : String + + /** + * The byte representation of this script token + * @return + */ def bytes = BitcoinSUtil.decodeHex(hex) + + /** + * The conversion from the byte representation of a token to a number + * @return + */ def toLong = BitcoinSUtil.hexToLong(hex) } +/** + * A script operation is an instruction that takes an input and gives an output + * Think of these as functions + */ trait ScriptOperation extends ScriptToken { def opCode : Int override def hex : String = BitcoinSUtil.encodeHex(opCode.toByte) } +/** + * A constant in the Script language for instance as String or a number + */ sealed trait ScriptConstant extends ScriptToken + +/** + * Represents a number in the Script language + */ sealed trait ScriptNumber extends ScriptConstant { + /** + * The underlying number of the ScriptNumber + * @return + */ def num : Long def + (that : ScriptNumber) : ScriptNumber = ScriptNumberFactory.fromNumber(num + that.num) @@ -34,6 +66,13 @@ sealed trait ScriptNumber extends ScriptConstant { def > (that : ScriptNumber) : Boolean = num > that.num def >= (that : ScriptNumber) : Boolean = num >= that.num + /** + * This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers + * are bitwise equivalent in Script. For instance ScriptNumber(0x01).numEqual(ScriptNumber(0x00000000001)) == true + * but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false + * @param that + * @return + */ def numEqual(that : ScriptNumber) : Boolean = num == that.num } @@ -46,11 +85,16 @@ sealed trait ScriptNumber extends ScriptConstant { case class ScriptNumberImpl(num : Long, override val hex : String) extends ScriptNumber +/** + * Companion object for ScriptNumberImpl that gives us access to more constructor types for the + * ScriptNumberImpl case class + */ object ScriptNumberImpl { def apply(num : Long) : ScriptNumber = ScriptNumberImpl(num, BitcoinSUtil.longToHex(num)) def apply(hex : String) : ScriptNumber = ScriptNumberImpl(BitcoinSUtil.hexToLong(hex), hex) def apply(bytes : Seq[Byte]) : ScriptNumber = ScriptNumberImpl(BitcoinSUtil.encodeHex(bytes)) } + sealed trait ScriptBoolean extends ScriptNumber //TODO: Need to remove ScriptTrue & ScriptFalse - make OP_TRUE/FALSE inherit from ScriptBoolean diff --git a/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala b/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala index ce2cfcafd3..78b090248d 100644 --- a/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala @@ -390,4 +390,16 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet } + it must "interpret two script constants as numbers and then add them" in { + val scriptConstant1 = ScriptConstantFactory.fromHex("ffffffff") + val scriptConstant2 = ScriptConstantFactory.fromHex("ffffff7f") + val stack = List(scriptConstant1, scriptConstant2) + val script = List(OP_ADD) + val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack, script) + val newProgram = opAdd(program) + newProgram.stack must be (List(OP_0)) + newProgram.script.isEmpty must be (true) + } + + } diff --git a/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala b/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala index 758d07d70a..3a9da4a6dd 100644 --- a/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala @@ -29,12 +29,12 @@ 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 = """ | - |[["0x4c 0x00","0 EQUAL", "P2SH,STRICTENC"]] - """.stripMargin*/ - val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close() + |[["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL", "P2SH,STRICTENC"]] + """.stripMargin + //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