From 471e31a7a068ecd2f7c227ede635d4797b3af8a0 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Mon, 11 Apr 2016 12:03:41 -0500 Subject: [PATCH] Refactoring Arithmetic interpretations to go through one of three higher order helper functions --- .../arithmetic/ArithmeticInterpreter.scala | 234 ++++++++---------- .../script/bitwise/BitwiseInterpreter.scala | 43 ++-- .../scalacoin/script/constant/Constants.scala | 14 +- .../interpreter/ScriptInterpreter.scala | 3 +- .../org/scalacoin/util/BitcoinSUtil.scala | 2 + .../scala/org/scalacoin/util/NumberUtil.scala | 8 +- .../ArithmeticInterpreterTest.scala | 101 ++------ .../bitwise/BitwiseInterpreterTset.scala | 23 +- .../interpreter/ScriptInterpreterTest.scala | 3 +- 9 files changed, 184 insertions(+), 247 deletions(-) diff --git a/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala b/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala index c5ea9b1d71..3e861bec12 100644 --- a/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala @@ -18,13 +18,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opAdd(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_ADD, "Script top must be OP_ADD") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_ADD") - - val b = numFromScriptToken(program.stack.head) - val a = numFromScriptToken(program.stack(1)) - - val result = ScriptNumberFactory.fromNumber(a + b) - ScriptProgramFactory.factory(program, result :: program.stack.slice(2,program.stack.size), - program.script.tail) + performBinaryArithmeticOperation(program, (x,y) => x + y) } /** @@ -35,12 +29,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def op1Add(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_1ADD, "Script top must be OP_1ADD") require(program.stack.size > 0, "Must have one item on the stack to execute OP_1ADD") - val newStackTop = program.stack.head match { - case s : ScriptNumber => s + ScriptNumberFactory.one - case x => throw new IllegalArgumentException("Stack must be script number to perform OP_1ADD, stack top was: " + x) - } - - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail) + performUnaryArithmeticOperation(program, x => x + ScriptNumberFactory.one) } /** @@ -51,13 +40,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def op1Sub(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_1SUB, "Script top must be OP_1SUB") require(program.stack.size > 0, "Stack size must be 1 or more perform an OP_1SUB") - - val newStackTop = program.stack.head match { - case s : ScriptNumber => s - ScriptNumberFactory.one - case x => throw new IllegalArgumentException("Stack must be script number to perform OP_1ADD, stack top was: " + x) - } - - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail) + performUnaryArithmeticOperation(program, x => x - ScriptNumberFactory.one ) } @@ -69,14 +52,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opSub(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_SUB, "Script top must be OP_SUB") require(program.stack.size > 1, "Stack must contain two elements to do an OP_SUB") - - val (b,a) = program.stack match { - case (h : ScriptNumber) :: (h1 : ScriptNumber) :: t => (h,h1) - case x => throw new IllegalArgumentException("Stack must be script number to perform OP_SUB, stack top was: " + x) - - } - val newScriptNumber = a - b - ScriptProgramFactory.factory(program, newScriptNumber :: program.stack.tail, program.script.tail) + performBinaryArithmeticOperation(program, (x,y) => y - x) } /** @@ -87,11 +63,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opAbs(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_ABS, "Script top must be OP_ABS") require(program.stack.size > 0, "Stack size must be 1 or more perform an OP_ABS") - val newStackTop = program.stack.head match { - case s : ScriptNumber => ScriptNumberFactory.fromNumber(s.num.abs) - case x => throw new IllegalArgumentException("Stack must be script number to perform OP_ABS, stack top was: " + x) - } - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail) + performUnaryArithmeticOperation(program, x => ScriptNumberFactory.fromNumber(x.num.abs)) } /** @@ -102,11 +74,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opNegate(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_NEGATE, "Script top must be OP_NEGATE") require(program.stack.size > 0, "Stack size must be 1 or more perform an OP_NEGATE") - val newStackTop = program.stack.head match { - case s : ScriptNumber => ScriptNumberFactory.fromNumber(-s.num) - case x => throw new IllegalArgumentException("Stack must be script number to perform OP_ABS, stack top was: " + x) - } - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail) + performUnaryArithmeticOperation(program, x => x -) } /** @@ -118,17 +86,7 @@ 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") //TODO: this needs to be modified to have an exhaustive type check - val newStackTop = program.stack.head match { - case OP_0 => OP_TRUE - case OP_FALSE => OP_TRUE - case OP_1 => OP_0 - case ScriptNumberFactory.zero => OP_TRUE - case ScriptNumberFactory.one => OP_FALSE - case ScriptFalse => ScriptTrue - case ScriptTrue => ScriptFalse - case _ => OP_FALSE - } - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail) + performUnaryArithmeticOperation(program, x => if (program.stackTopIsFalse) OP_TRUE else OP_FALSE) } /** @@ -139,12 +97,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def op0NotEqual(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_0NOTEQUAL, "Script top must be OP_0NOTEQUAL") require(program.stack.size > 0, "Stack size must be 1 or more perform an OP_0NOTEQUAL") - val newStackTop = program.stack.head match { - case OP_0 => OP_0 - case ScriptNumberFactory.zero => OP_0 - case _ => OP_1 - } - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail, program.script.tail) + performUnaryArithmeticOperation(program, x => if(x.num == 0) OP_FALSE else OP_TRUE) } @@ -156,12 +109,11 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opBoolAnd(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_BOOLAND, "Script top must be OP_BOOLAND") 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 aIsFalse = (a == ScriptNumberFactory.zero || a == OP_0) - val bIsFalse = (b == ScriptNumberFactory.zero || b == OP_0) - val newStackTop = if (aIsFalse || bIsFalse) OP_FALSE else OP_TRUE - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program,(x,y) => { + val xIsFalse = (x == ScriptNumberFactory.zero || x == OP_0) + val yIsFalse = (y == ScriptNumberFactory.zero || y == OP_0) + if (xIsFalse || yIsFalse) false else true + }) } @@ -173,10 +125,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opBoolOr(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_BOOLOR, "Script top must be OP_BOOLOR") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_BOOLOR") - val b = program.stack.head - val a = program.stack.tail.head - val newStackTop = if (a == b && (a == ScriptNumberFactory.zero || a == OP_0)) OP_0 else OP_1 - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + + performBinaryBooleanOperation(program, (x,y) => { + if (x == y && (x == ScriptNumberFactory.zero || x == OP_0)) false else true + }) } /** @@ -187,14 +139,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { def opNumEqual(program : ScriptProgram) : ScriptProgram = { require(program.script.headOption.isDefined && program.script.head == OP_NUMEQUAL, "Script top must be OP_NUMEQUAL") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_NUMEQUAL") - val b = program.stack.head - val a = program.stack.tail.head - val isSame = (a,b) match { - case (x : ScriptNumber, y : ScriptNumber) => x.num == y.num - case (x,y) => x == y - } - val newStackTop = if (isSame) OP_TRUE else OP_FALSE - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program,(x,y) => x.numEqual(y)) } @@ -225,17 +170,9 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { "Script top must be OP_NUMNOTEQUAL") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_NUMNOTEQUAL") - val b = program.stack.head - val a = program.stack.tail.head - - val isSame = (a,b) match { - 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_0 else OP_1 - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program, (x,y) => { + x.num != y.num + }) } @@ -248,17 +185,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { require(program.script.headOption.isDefined && program.script.head == OP_LESSTHAN, "Script top must be OP_LESSTHAN") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_LESSTHAN") - val b = program.stack.head - val a = program.stack.tail.head - - val isLessThan = (a,b) match { - case (x : ScriptNumberOperation, y : ScriptNumber) => x.scriptNumber < y - case (x : ScriptNumber, y : ScriptNumberOperation) => x < y.scriptNumber - case (x,y) => x.toLong < y.toLong - } - - val newStackTop = if (isLessThan) OP_1 else OP_0 - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program, (x,y) => y < x) } @@ -271,17 +198,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { require(program.script.headOption.isDefined && program.script.head == OP_GREATERTHAN, "Script top must be OP_GREATERTHAN") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_GREATERTHAN") - val b = program.stack.head - val a = program.stack.tail.head - - val isGreaterThan = (a,b) match { - case (x : ScriptNumberOperation, y : ScriptNumber) => x.scriptNumber > y - case (x : ScriptNumber, y : ScriptNumberOperation) => x > y.scriptNumber - case (x,y) => x.toLong > y.toLong - } - - val newStackTop = if (isGreaterThan) OP_1 else OP_0 - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program, (x,y) => y > x) } /** @@ -293,17 +210,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { require(program.script.headOption.isDefined && program.script.head == OP_LESSTHANOREQUAL, "Script top must be OP_LESSTHANOREQUAL") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_LESSTHANOREQUAL") - val b = program.stack.head - val a = program.stack.tail.head - - val isLessThanOrEqual = (a,b) match { - case (x : ScriptNumberOperation, y : ScriptNumber) => x.scriptNumber <= y - case (x : ScriptNumber, y : ScriptNumberOperation) => x <= y.scriptNumber - case (x,y) => x.toLong <= y.toLong - } - - val newStackTop = if (isLessThanOrEqual) OP_1 else OP_0 - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program, (x,y) => y <= x) } /** @@ -315,17 +222,7 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { require(program.script.headOption.isDefined && program.script.head == OP_GREATERTHANOREQUAL, "Script top must be OP_GREATERTHANOREQUAL") require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_GREATERTHANOREQUAL") - val b = program.stack.head - val a = program.stack.tail.head - - val isGreaterThanOrEqual = (a,b) match { - case (x : ScriptNumberOperation, y : ScriptNumber) => x.scriptNumber >= y - case (x : ScriptNumber, y : ScriptNumberOperation) => x >= y.scriptNumber - case (x,y) => x.toLong >= y.toLong - } - - val newStackTop = if (isGreaterThanOrEqual) OP_1 else OP_0 - ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail) + performBinaryBooleanOperation(program, (x,y) => y >= x) } @@ -409,4 +306,85 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter { case x : ScriptConstantImpl => Integer.parseInt(x.hex,16) } + + /** + * This function checks if a number is <= 4 bytes in size + * We cannot perform arithmetic operations on bitcoin numbers that are larger than 4 bytes. + * https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L214-L239 + * @param scriptNumber the script number to be checked + * @return if the number is larger than 4 bytes + */ + private def checkBitcoinIntByteSize(scriptNumber : ScriptNumber) : Boolean = scriptNumber.bytes.size <= 4 + + + /** + * Performs the given arithmetic operation on the stack head + * @param program the program whose stack top is used as an argument for the arithmetic operation + * @param op the arithmetic ooperation that needs to be executed on the number, for instance incrementing by 1 + * @return the program with the result from performing the arithmetic operation pushed onto the top of the stack + */ + private def performUnaryArithmeticOperation(program : ScriptProgram, op : ScriptNumber => ScriptNumber) : ScriptProgram = { + program.stack.head match { + case s : ScriptNumber => + if (checkBitcoinIntByteSize(s)) { + val newScriptNumber = op(s) + ScriptProgramFactory.factory(program, newScriptNumber :: program.stack.tail, program.script.tail) + } + else { + logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s) + ScriptProgramFactory.factory(program,false) + } + case s : ScriptToken => + logger.error("Stack top must be a script number to perform an arithmetic operation") + ScriptProgramFactory.factory(program,false) + } + } + + /** + * Performs the given arithmetic operation on the top two stack items + * @param program the program whose stack top is used as an argument for the arithmetic operation + * @param op the arithmetic ooperation that needs to be executed on the number, for instance incrementing by 1 + * @return the program with the result from performing the arithmetic operation pushed onto the top of the stack + */ + private def performBinaryArithmeticOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => ScriptNumber) : ScriptProgram = { + (program.stack.head, program.stack.tail.head) match { + case (x : ScriptNumber, y : ScriptNumber) => + if (checkBitcoinIntByteSize(x) && checkBitcoinIntByteSize(y)) { + val newStackTop = op(x,y) + ScriptProgramFactory.factory(program,newStackTop :: program.stack.tail.tail,program.script.tail) + } + else { + logger.error("Cannot perform arithmetic 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 : ScriptToken, y : ScriptToken) => + logger.error("The top two stack items must be script numbers to perform an arithmetic operation") + ScriptProgramFactory.factory(program,false) + } + } + + /** + * Compares two script numbers with the given boolean operation + * @param program the program whose two top stack elements are used for the comparison + * @param op the operation which compares the two script numbers + * @return the program with either ScriptFalse or ScriptTrue on the stack top + */ + private def performBinaryBooleanOperation(program : ScriptProgram, op : (ScriptNumber, ScriptNumber) => Boolean) : ScriptProgram = { + (program.stack.head, program.stack.tail.head) match { + case (x : ScriptNumber, y : ScriptNumber) => + if (checkBitcoinIntByteSize(x) && checkBitcoinIntByteSize(y)) { + val newStackTop = if(op(x,y)) OP_TRUE else OP_FALSE + ScriptProgramFactory.factory(program,newStackTop :: program.stack.tail.tail,program.script.tail) + } + else { + 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 : 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/bitwise/BitwiseInterpreter.scala b/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala index 6b055ebc91..3a9bf48125 100644 --- a/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala @@ -17,27 +17,36 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter { * @return */ def opEqual(program : ScriptProgram) : ScriptProgram = { - require(program.stack.size > 1, "Stack size must be 2 or more to compare the top two values for OP_EQUAL") require(program.script.headOption.isDefined && program.script.head == OP_EQUAL, "Script operation must be OP_EQUAL") - logger.debug("Original stack: " + program.stack) - val h = program.stack.head - val h1 = program.stack.tail.head - - val result = (h,h1) match { - case (OP_0, x) => OP_0.hex == x.hex - case (x, OP_0) => x.hex == OP_0.hex - case (OP_1,x) => OP_1.scriptNumber == x - case (x,OP_1) => x == OP_1.scriptNumber -/* case (x : ScriptConstant, y : ScriptConstant) => x == y - case (ScriptConstantImpl(x), y : ScriptNumber) => BitcoinSUtil.hexToLong(x) == y.num - case (x : ScriptNumber, y : ScriptConstant) => x.num == BitcoinSUtil.hexToLong(y.hex)*/ - case _ => h.bytes == h1.bytes + if (program.stack.size < 2) { + ScriptProgramFactory.factory(program,false) + } else { + val h = program.stack.head + val h1 = program.stack.tail.head + val result = (h,h1) match { + case (OP_0,ScriptNumberFactory.zero) | (ScriptNumberFactory.zero, OP_0) => + OP_0.scriptNumber == ScriptNumberFactory.zero + case (OP_FALSE,ScriptNumberFactory.zero) | (ScriptNumberFactory.zero, OP_FALSE) => + OP_FALSE.scriptNumber == ScriptNumberFactory.zero + case (OP_TRUE,ScriptNumberFactory.one) | (ScriptNumberFactory.one, OP_TRUE) => + OP_TRUE.scriptNumber == ScriptNumberFactory.one + case (OP_1, ScriptNumberFactory.one) | (ScriptNumberFactory.one, OP_1) => + OP_1.scriptNumber == ScriptNumberFactory.one + case (ScriptFalse, ScriptNumberFactory.zero) | (ScriptNumberFactory.zero, ScriptFalse) => + ScriptFalse.num == ScriptNumberFactory.zero.num + case (ScriptFalse, OP_0) | (OP_0, ScriptFalse) => + ScriptFalse.num == OP_0.num + case (ScriptTrue, ScriptNumberFactory.one) | (ScriptNumberFactory.one, ScriptTrue) => + ScriptTrue.num == ScriptNumberFactory.one.num + case (ScriptTrue, OP_1) | (OP_1, ScriptTrue) => + ScriptTrue.num == OP_1.num + case _ => h.bytes == h1.bytes + } + val scriptBoolean : ScriptBoolean = if (result) ScriptTrue else ScriptFalse + ScriptProgramFactory.factory(program,scriptBoolean :: program.stack.tail.tail, program.script.tail) } - val scriptBoolean : ScriptBoolean = if (result) ScriptTrue else ScriptFalse - - ScriptProgramFactory.factory(program,scriptBoolean :: program.stack.tail.tail, program.script.tail) } diff --git a/src/main/scala/org/scalacoin/script/constant/Constants.scala b/src/main/scala/org/scalacoin/script/constant/Constants.scala index 32cd3a8aa2..57ce783524 100644 --- a/src/main/scala/org/scalacoin/script/constant/Constants.scala +++ b/src/main/scala/org/scalacoin/script/constant/Constants.scala @@ -25,6 +25,8 @@ sealed trait ScriptNumber extends ScriptConstant { def num : Long def + (that : ScriptNumber) : ScriptNumber = ScriptNumberFactory.fromNumber(num + that.num) + + def - = ScriptNumberFactory.fromNumber(-num) def - (that : ScriptNumber) : ScriptNumber = ScriptNumberFactory.fromNumber(num - that.num) def * (that : ScriptNumber) : ScriptNumber = ScriptNumberFactory.fromNumber(num * that.num) @@ -32,6 +34,8 @@ sealed trait ScriptNumber extends ScriptConstant { def <= (that : ScriptNumber) : Boolean = num <= that.num def > (that : ScriptNumber) : Boolean = num > that.num def >= (that : ScriptNumber) : Boolean = num >= that.num + + def numEqual(that : ScriptNumber) : Boolean = num == that.num } /** @@ -42,19 +46,23 @@ sealed trait ScriptNumber extends ScriptConstant { */ case class ScriptNumberImpl(num : Long, override val hex : String) extends ScriptNumber + 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 ScriptConstant +sealed trait ScriptBoolean extends ScriptNumber +//TODO: Need to remove ScriptTrue & ScriptFalse - make OP_TRUE/FALSE inherit from ScriptBoolean case object ScriptTrue extends ScriptBoolean { - override def hex = "01" + override def hex = OP_TRUE.hex + override def num = OP_TRUE.num } case object ScriptFalse extends ScriptBoolean { - override def hex = "00" + override def hex = OP_FALSE.hex + override def num = OP_FALSE.num } /** diff --git a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala index beeb7e08af..80af74ae5a 100644 --- a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala @@ -58,10 +58,11 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con case _ if !program.script.intersect(Seq(OP_CAT,OP_SUBSTR,OP_LEFT,OP_RIGHT)).isEmpty => logger.error("Script is invalid because it contains a disabled splice operation") (false,ScriptProgramFactory.factory(program,false)) - + //disabled bitwise operations case _ if !program.script.intersect(Seq(OP_INVERT, OP_AND, OP_OR, OP_XOR)).isEmpty => logger.error("Script is invalid because it contains a disabled bitwise operation") (false,ScriptProgramFactory.factory(program,false)) + //disabled arithmetic operations case _ if !program.script.intersect(Seq(OP_MUL, OP_2MUL, OP_DIV, OP_2DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT)).isEmpty => logger.error("Script is invalid because it contains a disabled arithmetic operation") (false,ScriptProgramFactory.factory(program,false)) diff --git a/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala b/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala index bc7bbc5151..42b8059079 100644 --- a/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala +++ b/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala @@ -45,6 +45,8 @@ trait BitcoinSUtil extends NumberUtil { def hexToLong(hex : String) : Long = toLong(hex) + def hexToInt(hex : String) : Int = toLong(hex).toInt + def decodeBase58(base58 : String) : Seq[Byte] = Base58.decode(base58).toList def encodeBase58(bytes : Seq[Byte]) : String = Base58.encode(bytes.toArray) diff --git a/src/main/scala/org/scalacoin/util/NumberUtil.scala b/src/main/scala/org/scalacoin/util/NumberUtil.scala index 2ea55bf5d3..d03572b195 100644 --- a/src/main/scala/org/scalacoin/util/NumberUtil.scala +++ b/src/main/scala/org/scalacoin/util/NumberUtil.scala @@ -49,12 +49,12 @@ trait NumberUtil extends BitcoinSLogger { */ def longToHex(long : Long) : String = { if (long > -1) { - val bytes = toByteList(long) + val bytes = toByteSeq(long) BitcoinSUtil.flipEndianess(BitcoinSUtil.encodeHex(bytes)) } else { - val bytes = toByteList(long.abs) + val bytes = toByteSeq(long.abs) //add sign bit - val negativeNumberBytes : List[Byte] = changeSignBitToNegative(bytes) + val negativeNumberBytes : List[Byte] = changeSignBitToNegative(bytes.toList) val hex = BitcoinSUtil.encodeHex(negativeNumberBytes.reverse) hex } @@ -109,7 +109,7 @@ trait NumberUtil extends BitcoinSLogger { } - def toByteList(long : Long) = BigInt(long).toByteArray.toList + def toByteSeq(long : Long) : Seq[Byte] = BigInt(long).toByteArray /** diff --git a/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala b/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala index d6337e7ee1..c1198b663e 100644 --- a/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala @@ -19,36 +19,6 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet newProgram.script.isEmpty must be (true) } -/* it must "perform an OP_ADD correctly on ScriptConstantImpl" in { - //0x64 is the hexadecimal representation for 100 - val stack = List(ScriptConstantImpl("64"), ScriptConstantImpl("64")) - val script = List(OP_ADD) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = opAdd(program) - //0xC8 is 200 in hex - newProgram.stack.head must be (ScriptNumberImpl(200)) - newProgram.script.isEmpty must be (true) - }*/ - - it must "perform an OP_ADD correctly on a ScriptConstant & ScriptNumber that are used as the args" in { - val stack = List(ScriptNumberFactory.one, ScriptConstantImpl("64")) - val script = List(OP_ADD) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = opAdd(program) - //0x65 is 101 in hex - newProgram.stack.head must be (ScriptNumberFactory.fromNumber(101)) - newProgram.script.isEmpty must be (true) - } - - it must "perform an OP_ADD correctly on a a negative number" in { - val stack = List(ScriptConstantImpl("3e8"), ScriptNumberFactory.fromNumber(-1)) - val script = List(OP_ADD) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = opAdd(program) - - newProgram.stack.head must be (ScriptNumberFactory.fromNumber(999)) - newProgram.script.isEmpty must be (true) - } it must "perform an OP_1ADD correctly" in { val stack = List(ScriptNumberFactory.zero) @@ -68,14 +38,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val newProgram = op1Add(program) } } - it must "throw an exception if we have an OP_1ADD with a non script number on the stack" in { - intercept[IllegalArgumentException] { - val stack = List(ScriptConstantImpl("nan")) - val script = List(OP_1ADD) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = op1Add(program) - } - } + it must "perform an OP_1SUB corectly" in { val stack = List(ScriptNumberFactory.zero) val script = List(OP_1SUB) @@ -95,14 +58,6 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet } } - it must "throw an exception if we have an OP_1SUB with a non script number on the stack" in { - intercept[IllegalArgumentException] { - val stack = List(ScriptConstantImpl("nan")) - val script = List(OP_1SUB) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = op1Sub(program) - } - } it must "perform an OP_SUB corectly" in { val stack = List(ScriptNumberFactory.one,ScriptNumberFactory.zero) val script = List(OP_SUB) @@ -123,12 +78,12 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet } it must "throw an exception if we have an OP_SUB with one of the two numbers a non script number on the stack" in { - intercept[IllegalArgumentException] { - val stack = List(ScriptNumberFactory.one, ScriptConstantImpl("nan")) - val script = List(OP_SUB) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = opSub(program) - } + + val stack = List(ScriptNumberFactory.one, ScriptConstantImpl("nan")) + val script = List(OP_SUB) + val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) + val newProgram = opSub(program) + newProgram.isValid must be (false) } it must "perform an OP_ABS on a negative number corectly" in { @@ -159,14 +114,6 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet } } - it must "throw an exception if we have an OP_ABS with a non script number on the stack" in { - intercept[IllegalArgumentException] { - val stack = List(ScriptConstantImpl("nan")) - val script = List(OP_ABS) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = opAbs(program) - } - } it must "perform an OP_NEGATE on a zero correctly" in { val stack = List(ScriptNumberFactory.zero) val script = List(OP_NEGATE) @@ -205,14 +152,6 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet } } - it must "throw an exception if we have an OP_NEGATE with a non script number on the stack" in { - intercept[IllegalArgumentException] { - val stack = List(ScriptConstantImpl("nan")) - val script = List(OP_NEGATE) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - val newProgram = opNegate(program) - } - } it must "perform an OP_NOT correctly where 0 is the stack top" in { val stack = List(ScriptNumberFactory.zero) val script = List(OP_NOT) @@ -241,7 +180,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = op0NotEqual(program) - newProgram.stack.head must be (OP_0) + newProgram.stack.head must be (OP_FALSE) newProgram.script.isEmpty must be (true) } @@ -251,7 +190,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = op0NotEqual(program) - newProgram.stack.head must be (OP_1) + newProgram.stack.head must be (OP_TRUE) newProgram.script.isEmpty must be (true) } @@ -303,7 +242,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opBoolOr(program) - newProgram.stack.head must be (OP_1) + newProgram.stack.head must be (OP_TRUE) newProgram.script.isEmpty must be (true) } @@ -313,7 +252,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opBoolOr(program) - newProgram.stack.head must be (OP_0) + newProgram.stack.head must be (OP_FALSE) newProgram.script.isEmpty must be (true) } @@ -323,7 +262,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opBoolOr(program) - newProgram.stack.head must be (OP_1) + newProgram.stack.head must be (OP_TRUE) newProgram.script.isEmpty must be (true) } @@ -354,7 +293,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opNumNotEqual(program) - newProgram.stack.head must be (OP_0) + newProgram.stack.head must be (OP_FALSE) newProgram.script.isEmpty must be (true) } @@ -366,7 +305,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opNumNotEqual(program) - newProgram.stack.head must be (OP_1) + newProgram.stack.head must be (OP_TRUE) newProgram.script.isEmpty must be (true) } @@ -376,7 +315,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opLessThan(program) - newProgram.stack.head must be (OP_0) + newProgram.stack.head must be (OP_FALSE) newProgram.script.isEmpty must be (true) val stack1 = List(OP_0, ScriptNumberFactory.zero) @@ -385,7 +324,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program1 = ScriptProgramFactory.factory(TestUtil.testProgram,stack1,script1) val newProgram1 = opLessThan(program1) - newProgram1.stack.head must be (OP_0) + newProgram1.stack.head must be (OP_FALSE) newProgram1.script.isEmpty must be (true) val stack2 = List(OP_1, ScriptNumberImpl(0)) @@ -393,7 +332,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program2 = ScriptProgramFactory.factory(TestUtil.testProgram, stack2,script2) val newProgram2 = opLessThan(program2) - newProgram2.stack.head must be (OP_1) + newProgram2.stack.head must be (OP_TRUE) newProgram2.script.isEmpty must be (true) } @@ -403,7 +342,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) val newProgram = opGreaterThan(program) - newProgram.stack.head must be (OP_1) + newProgram.stack.head must be (OP_TRUE) newProgram.script.isEmpty must be (true) val stack1 = List(OP_0, ScriptNumberFactory.zero) @@ -411,7 +350,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program1 = ScriptProgramFactory.factory(TestUtil.testProgram,stack1,script1) val newProgram1 = opGreaterThan(program1) - newProgram1.stack.head must be (OP_0) + newProgram1.stack.head must be (OP_FALSE) newProgram1.script.isEmpty must be (true) val stack2 = List(OP_1, ScriptNumberFactory.zero) @@ -419,7 +358,7 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet val program2 = ScriptProgramFactory.factory(TestUtil.testProgram, stack2,script2) val newProgram2 = opGreaterThan(program2) - newProgram2.stack.head must be (OP_0) + newProgram2.stack.head must be (OP_FALSE) newProgram2.script.isEmpty must be (true) } diff --git a/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala b/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala index 60457d596e..fda4301fbc 100644 --- a/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala +++ b/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala @@ -20,6 +20,15 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte newProgram.stack.head must be (ScriptTrue) } + + it must "evaluate OP_1 and OP_TRUE to equal" in { + val stack = List(OP_1, OP_TRUE) + val script = List(OP_EQUAL) + val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack, script) + val newProgram = opEqual(program) + newProgram.stack.head must be (ScriptTrue) + } + it must "throw an exception for OP_EQUAL when we don't have enough items on the stack" in { intercept[IllegalArgumentException] { opEqual(ScriptProgramFactory.factory(TestUtil.testProgram, List(),List())) @@ -52,27 +61,19 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte it must "evaluate a ScriptNumber & ScriptConstant to true if they are the same" in { - val stack = List(ScriptNumberImpl(2), ScriptConstantImpl("02")) + val stack = List(ScriptNumberFactory.fromNumber(2), ScriptConstantImpl("02")) val script = List(OP_EQUAL) val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) opEqual(program).stack.head must be (ScriptTrue) - val stack1 = List( ScriptConstantImpl("02"),ScriptNumberImpl(2)) + val stack1 = List( ScriptConstantImpl("02"),ScriptNumberFactory.fromNumber(2)) val script1 = List(OP_EQUAL) val program1 = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) opEqual(program1).stack.head must be (ScriptTrue) } it must "evaluate an OP_0 and ScriptNumberImpl(0) to equal" in { - val stack = List(OP_0, ScriptNumberImpl(0)) - val script = List(OP_EQUAL) - val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) - opEqual(program).stack.head must be (ScriptTrue) - } - - - it must "evaluate an OP_1 and ScriptNumberImpl(1) to equal" in { - val stack = List(OP_1, ScriptNumberImpl(1)) + val stack = List(OP_0, ScriptNumberFactory.zero) val script = List(OP_EQUAL) val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script) opEqual(program).stack.head must be (ScriptTrue) diff --git a/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala b/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala index dcaf3f2e3e..9db7be69ae 100644 --- a/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/interpreter/ScriptInterpreterTest.scala @@ -32,7 +32,7 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp /* val lines = """ | - |[["0 1", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP", "P2SH,STRICTENC"]] + |[["1", "0x02 0x0100 EQUAL NOT", "P2SH,STRICTENC", "Not the same byte array..."]] """.stripMargin*/ val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close() val json = lines.parseJson @@ -80,7 +80,6 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp |[["1 IF 0 ENDIF", "1 ENDIF", "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]]]