mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 23:08:31 +01:00
Refactoring Arithmetic interpretations to go through one of three higher order helper functions
This commit is contained in:
parent
8e48598785
commit
471e31a7a0
9 changed files with 184 additions and 247 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]]]
|
||||
|
|
Loading…
Add table
Reference in a new issue