Refactoring Arithmetic interpretations to go through one of three higher order helper functions

This commit is contained in:
Chris Stewart 2016-04-11 12:03:41 -05:00
parent 8e48598785
commit 471e31a7a0
9 changed files with 184 additions and 247 deletions

View file

@ -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)
}
}
}

View file

@ -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)
}

View file

@ -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
}
/**

View file

@ -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))

View file

@ -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)

View file

@ -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
/**

View file

@ -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)
}

View file

@ -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)

View file

@ -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]]]