mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 15:02:17 +01:00
Refactoring old requirements on stack sizes to log errors and return script programs that are marked invalid
This commit is contained in:
parent
a25ed9aaef
commit
994c73062a
7 changed files with 255 additions and 212 deletions
|
@ -18,7 +18,6 @@ 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")
|
||||
performBinaryArithmeticOperation(program, (x,y) => x + y)
|
||||
}
|
||||
|
||||
|
@ -29,7 +28,6 @@ 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")
|
||||
performUnaryArithmeticOperation(program, x => x + ScriptNumberFactory.one)
|
||||
}
|
||||
|
||||
|
@ -40,7 +38,6 @@ 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")
|
||||
performUnaryArithmeticOperation(program, x => x - ScriptNumberFactory.one )
|
||||
}
|
||||
|
||||
|
@ -52,7 +49,6 @@ 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")
|
||||
performBinaryArithmeticOperation(program, (x,y) => y - x)
|
||||
}
|
||||
|
||||
|
@ -63,7 +59,6 @@ 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")
|
||||
performUnaryArithmeticOperation(program, x => ScriptNumberFactory.fromNumber(x.num.abs))
|
||||
}
|
||||
|
||||
|
@ -74,7 +69,6 @@ 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")
|
||||
performUnaryArithmeticOperation(program, x => x -)
|
||||
}
|
||||
|
||||
|
@ -85,7 +79,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
*/
|
||||
def opNot(program : ScriptProgram) : ScriptProgram = {
|
||||
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
|
||||
performUnaryArithmeticOperation(program, x => if (program.stackTopIsFalse) OP_TRUE else OP_FALSE)
|
||||
}
|
||||
|
@ -97,7 +90,6 @@ 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")
|
||||
performUnaryArithmeticOperation(program, x => if(x.num == 0) OP_FALSE else OP_TRUE)
|
||||
}
|
||||
|
||||
|
@ -109,7 +101,6 @@ 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")
|
||||
performBinaryBooleanOperation(program,(x,y) => {
|
||||
val xIsFalse = (x == ScriptNumberFactory.zero || x == OP_0)
|
||||
val yIsFalse = (y == ScriptNumberFactory.zero || y == OP_0)
|
||||
|
@ -125,8 +116,6 @@ 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")
|
||||
|
||||
performBinaryBooleanOperation(program, (x,y) => {
|
||||
if (x == y && (x == ScriptNumberFactory.zero || x == OP_0)) false else true
|
||||
})
|
||||
|
@ -139,7 +128,6 @@ 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")
|
||||
performBinaryBooleanOperation(program,(x,y) => x.numEqual(y))
|
||||
}
|
||||
|
||||
|
@ -152,12 +140,17 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opNumEqualVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NUMEQUALVERIFY,
|
||||
"Script top must be OP_NUMEQUALVERIFY")
|
||||
require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_NUMEQUALVERIFY")
|
||||
val numEqualProgram = ScriptProgramFactory.factory(program, program.stack, OP_NUMEQUAL :: program.script.tail)
|
||||
val numEqualResult = opNumEqual(numEqualProgram)
|
||||
val verifyProgram = ScriptProgramFactory.factory(numEqualResult, numEqualResult.stack, OP_VERIFY :: numEqualResult.script)
|
||||
val verifyResult = opVerify(verifyProgram)
|
||||
verifyResult
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_NUMEQUALVERIFY requires two stack elements")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
val numEqualProgram = ScriptProgramFactory.factory(program, program.stack, OP_NUMEQUAL :: program.script.tail)
|
||||
val numEqualResult = opNumEqual(numEqualProgram)
|
||||
val verifyProgram = ScriptProgramFactory.factory(numEqualResult, numEqualResult.stack, OP_VERIFY :: numEqualResult.script)
|
||||
val verifyResult = opVerify(verifyProgram)
|
||||
verifyResult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -169,8 +162,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opNumNotEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_NUMNOTEQUAL,
|
||||
"Script top must be OP_NUMNOTEQUAL")
|
||||
require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_NUMNOTEQUAL")
|
||||
|
||||
performBinaryBooleanOperation(program, (x,y) => {
|
||||
x.num != y.num
|
||||
})
|
||||
|
@ -185,7 +176,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opLessThan(program : ScriptProgram) : ScriptProgram = {
|
||||
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")
|
||||
performBinaryBooleanOperation(program, (x,y) => y < x)
|
||||
}
|
||||
|
||||
|
@ -198,7 +188,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opGreaterThan(program : ScriptProgram) : ScriptProgram = {
|
||||
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")
|
||||
performBinaryBooleanOperation(program, (x,y) => y > x)
|
||||
}
|
||||
|
||||
|
@ -210,7 +199,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opLessThanOrEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
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")
|
||||
performBinaryBooleanOperation(program, (x,y) => y <= x)
|
||||
}
|
||||
|
||||
|
@ -222,7 +210,6 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opGreaterThanOrEqual(program : ScriptProgram) : ScriptProgram = {
|
||||
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")
|
||||
performBinaryBooleanOperation(program, (x,y) => y >= x)
|
||||
}
|
||||
|
||||
|
@ -235,18 +222,23 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opMin(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_MIN,
|
||||
"Script top must be OP_MIN")
|
||||
require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_MIN")
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
|
||||
val isLessThanOrEqual = (a,b) match {
|
||||
case (x : ScriptNumberOperation, y : ScriptNumber) => x.num <= y.num
|
||||
case (x : ScriptNumber, y : ScriptNumberOperation) => x.num <= y.num
|
||||
case (x,y) => x.toLong <= y.toLong
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MIN requires at least two stack elements")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
val isLessThanOrEqual = (a,b) match {
|
||||
case (x : ScriptNumberOperation, y : ScriptNumber) => x.num <= y.num
|
||||
case (x : ScriptNumber, y : ScriptNumberOperation) => x.num <= y.num
|
||||
case (x,y) => x.toLong <= y.toLong
|
||||
}
|
||||
|
||||
val newStackTop = if (isLessThanOrEqual) a else b
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
val newStackTop = if (isLessThanOrEqual) a else b
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,19 +249,25 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
*/
|
||||
def opMax(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_MAX,
|
||||
"Script top must be OP_MIN")
|
||||
require(program.stack.size > 1, "Stack size must be 2 or more perform an OP_MIN")
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
"Script top must be OP_MAX")
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_MAX requires at least two stack elements")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
val b = program.stack.head
|
||||
val a = program.stack.tail.head
|
||||
|
||||
val isGreaterThanOrEqual = (a,b) match {
|
||||
case (x : ScriptNumberOperation, y : ScriptNumber) => x.num >= y.num
|
||||
case (x : ScriptNumber, y : ScriptNumberOperation) => x.num >= y.num
|
||||
case (x,y) => x.toLong >= y.toLong
|
||||
val isGreaterThanOrEqual = (a,b) match {
|
||||
case (x : ScriptNumberOperation, y : ScriptNumber) => x.num >= y.num
|
||||
case (x : ScriptNumber, y : ScriptNumberOperation) => x.num >= y.num
|
||||
case (x,y) => x.toLong >= y.toLong
|
||||
}
|
||||
|
||||
val newStackTop = if (isGreaterThanOrEqual) a else b
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
val newStackTop = if (isGreaterThanOrEqual) a else b
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail, program.script.tail)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -281,30 +279,23 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
def opWithin(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_WITHIN,
|
||||
"Script top must be OP_WITHIN")
|
||||
require(program.stack.size > 2, "Stack size must be 3 or more perform an OP_WITHIN")
|
||||
if (program.stack.size < 3) {
|
||||
logger.error("OP_WITHIN requires at least 3 elements on the stack")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
val c = program.stack.head
|
||||
val b = program.stack.tail.head
|
||||
val a = program.stack.tail.tail.head
|
||||
|
||||
val c = program.stack.head
|
||||
val b = program.stack.tail.head
|
||||
val a = program.stack.tail.tail.head
|
||||
val isWithinRange = (a,b,c) match {
|
||||
case (x : ScriptNumber, y : ScriptNumber, z : ScriptNumber) => x >= y && x < z
|
||||
case (x,y,z) => x.toLong >= y.toLong && x.toLong < z.toLong
|
||||
}
|
||||
|
||||
val isWithinRange = (a,b,c) match {
|
||||
case (x : ScriptNumber, y : ScriptNumber, z : ScriptNumber) => x >= y && x < z
|
||||
case (x,y,z) => x.toLong >= y.toLong && x.toLong < z.toLong
|
||||
val newStackTop = if (isWithinRange) OP_TRUE else OP_FALSE
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
val newStackTop = if (isWithinRange) OP_TRUE else OP_FALSE
|
||||
ScriptProgramFactory.factory(program, newStackTop :: program.stack.tail.tail.tail, program.script.tail)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a script token to an integer
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
private def numFromScriptToken(token : ScriptToken) : Long = token match {
|
||||
case x : ScriptNumber => x.num
|
||||
case x : ScriptConstantImpl => Integer.parseInt(x.hex,16)
|
||||
}
|
||||
|
||||
|
||||
|
@ -325,8 +316,11 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
* @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 =>
|
||||
program.stack.headOption match {
|
||||
case None =>
|
||||
logger.error("We need one stack element for performing a unary arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
case Some(s : ScriptNumber) =>
|
||||
if (checkBitcoinIntByteSize(s)) {
|
||||
val newScriptNumber = op(s)
|
||||
ScriptProgramFactory.factory(program, newScriptNumber :: program.stack.tail, program.script.tail)
|
||||
|
@ -335,11 +329,11 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s)
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
case s : ScriptConstant =>
|
||||
case Some(s : ScriptConstant) =>
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performUnaryArithmeticOperation(newProgram, op)
|
||||
case s : ScriptToken =>
|
||||
case Some(s : ScriptToken) =>
|
||||
logger.error("Stack top must be a script number to perform an arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
|
@ -352,36 +346,43 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
* @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)
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("We must have two elements to perform a binary arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
(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 : ScriptConstant, y : ScriptNumber) =>
|
||||
//interpret x as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptNumber, y : ScriptConstant) =>
|
||||
//interpret y as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptConstant, y : ScriptConstant) =>
|
||||
//interpret x and y as a number
|
||||
val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
case (x : ScriptConstant, y : ScriptNumber) =>
|
||||
//interpret x as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptNumber, y : ScriptConstant) =>
|
||||
//interpret y as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptConstant, y : ScriptConstant) =>
|
||||
//interpret x and y as a number
|
||||
val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryArithmeticOperation(newProgram, op)
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -391,35 +392,43 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
|
|||
* @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)
|
||||
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("We need two stack elements for a binary boolean operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
(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 : ScriptConstant, y : ScriptNumber) =>
|
||||
//interpret x as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptNumber, y : ScriptConstant) =>
|
||||
//interpret y as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptConstant, y : ScriptConstant) =>
|
||||
//interpret x and y as a number
|
||||
val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
case (x : ScriptConstant, y : ScriptNumber) =>
|
||||
//interpret x as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptNumber, y : ScriptConstant) =>
|
||||
//interpret y as a number
|
||||
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptConstant, y : ScriptConstant) =>
|
||||
//interpret x and y as a number
|
||||
val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
|
||||
val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
|
||||
val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack)
|
||||
performBinaryBooleanOperation(newProgram, op)
|
||||
case (x : ScriptToken, y : ScriptToken) =>
|
||||
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,13 +56,16 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
|
|||
* @return
|
||||
*/
|
||||
def opEqualVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.stack.size > 1, "Stack size must be 2 or more to compare the top two values")
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_EQUALVERIFY, "Script operation must be OP_EQUALVERIFY")
|
||||
//first replace OP_EQUALVERIFY with OP_EQUAL and OP_VERIFY
|
||||
val simpleScript = OP_EQUAL :: OP_VERIFY :: program.script.tail
|
||||
val newProgram: ScriptProgram = opEqual(ScriptProgramFactory.factory(program, program.stack, simpleScript))
|
||||
opVerify(newProgram)
|
||||
program.stack.size > 1 match {
|
||||
case true =>
|
||||
//first replace OP_EQUALVERIFY with OP_EQUAL and OP_VERIFY
|
||||
val simpleScript = OP_EQUAL :: OP_VERIFY :: program.script.tail
|
||||
val newProgram: ScriptProgram = opEqual(ScriptProgramFactory.factory(program, program.stack, simpleScript))
|
||||
opVerify(newProgram)
|
||||
case false =>
|
||||
logger.error("OP_EQUALVERIFY requires at least 2 elements on the stack")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
logger.error("We do not have a matching OP_ENDIF for every OP_IF we have")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else if (program.stack.isEmpty) {
|
||||
logger.error("We do not have any stack elements for our OP_IF")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
else if (program.stackTopIsTrue) {
|
||||
logger.debug("OP_IF stack top was true")
|
||||
|
@ -58,6 +61,9 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
if (!checkMatchingOpIfOpNotIfOpEndIf(program.originalScript)) {
|
||||
logger.error("We do not have a matching OP_ENDIF for every OP_NOTIF we have")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else if (program.stack.isEmpty) {
|
||||
logger.error("We do not have any stack elements for our OP_NOTIF")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else if (program.stackTopIsTrue) {
|
||||
//remove the OP_NOTIF
|
||||
val scriptWithoutOpIf : BinaryTree[ScriptToken] = removeFirstOpIf(binaryTree)
|
||||
|
@ -134,13 +140,17 @@ trait ControlOperationsInterpreter extends BitcoinSLogger {
|
|||
* @return
|
||||
*/
|
||||
def opVerify(program : ScriptProgram) : ScriptProgram = {
|
||||
//TODO: There is a bug here, if the value is cast to an int and is != 0 then we need to pop the cast values
|
||||
//off of the stack..
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_VERIFY, "Script top must be OP_VERIFY")
|
||||
require(program.stack.size > 0, "Stack must have at least one element on it to run OP_VERIFY")
|
||||
logger.debug("Stack for OP_VERIFY: " + program.stack)
|
||||
if (program.stackTopIsFalse) ScriptProgramFactory.factory(program,false)
|
||||
else ScriptProgramFactory.factory(program, program.stack.tail,program.script.tail)
|
||||
program.script.size > 0 match {
|
||||
case true =>
|
||||
logger.debug("Stack for OP_VERIFY: " + program.stack)
|
||||
if (program.stackTopIsFalse) ScriptProgramFactory.factory(program,false)
|
||||
else ScriptProgramFactory.factory(program, program.stack.tail,program.script.tail)
|
||||
case false =>
|
||||
logger.error("OP_VERIFY requires an element to be on the stack")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -78,36 +78,40 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
*/
|
||||
def opCheckSig(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKSIG, "Script top must be OP_CHECKSIG")
|
||||
require(program.stack.size > 1, "Stack must have at least 2 items on it for OP_CHECKSIG")
|
||||
|
||||
|
||||
val pubKey = ECFactory.publicKey(program.stack.head.bytes)
|
||||
val signature = ECFactory.digitalSignature(program.stack.tail.head.bytes)
|
||||
|
||||
if (program.flags.contains(ScriptVerifyDerSig) && !DERSignatureUtil.isStrictDEREncoding(signature)) {
|
||||
//this means all of the signatures must encoded according to BIP66 strict dersig
|
||||
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
//script verification fails since the sig is not strictly der encoded
|
||||
logger.warn("Since the ScriptVerifyDerSig flag is set the signature being checked must be a strict dersig signature as per BIP 66\n" +
|
||||
"Sig: " + signature.hex)
|
||||
if (program.stack.size < 2) {
|
||||
logger.error("OP_CHECKSIG requires at lest two stack elements")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
val restOfStack = program.stack.tail.tail
|
||||
val pubKey = ECFactory.publicKey(program.stack.head.bytes)
|
||||
val signature = ECFactory.digitalSignature(program.stack.tail.head.bytes)
|
||||
|
||||
if (program.flags.contains(ScriptVerifyDerSig) && !DERSignatureUtil.isStrictDEREncoding(signature)) {
|
||||
//this means all of the signatures must encoded according to BIP66 strict dersig
|
||||
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
//script verification fails since the sig is not strictly der encoded
|
||||
logger.warn("Since the ScriptVerifyDerSig flag is set the signature being checked must be a strict dersig signature as per BIP 66\n" +
|
||||
"Sig: " + signature.hex)
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
val restOfStack = program.stack.tail.tail
|
||||
|
||||
|
||||
val result = TransactionSignatureChecker.checkSignature(program.txSignatureComponent,pubKey,
|
||||
signature,program.flags)
|
||||
logger.debug("signature verification isValid: " + result)
|
||||
result match {
|
||||
case SignatureValidationSuccess => ScriptProgramFactory.factory(program,
|
||||
ScriptTrue :: restOfStack,program.script.tail)
|
||||
case SignatureValidationFailureNotStrictDerEncoding =>
|
||||
ScriptProgramFactory.factory(program, ScriptFalse :: restOfStack,
|
||||
program.script.tail,SignatureValidationFailureNotStrictDerEncoding.isValid)
|
||||
case SignatureValidationFailureIncorrectSignatures =>
|
||||
ScriptProgramFactory.factory(program, ScriptFalse :: restOfStack,program.script.tail)
|
||||
val result = TransactionSignatureChecker.checkSignature(program.txSignatureComponent,pubKey,
|
||||
signature,program.flags)
|
||||
logger.debug("signature verification isValid: " + result)
|
||||
result match {
|
||||
case SignatureValidationSuccess => ScriptProgramFactory.factory(program,
|
||||
ScriptTrue :: restOfStack,program.script.tail)
|
||||
case SignatureValidationFailureNotStrictDerEncoding =>
|
||||
ScriptProgramFactory.factory(program, ScriptFalse :: restOfStack,
|
||||
program.script.tail,SignatureValidationFailureNotStrictDerEncoding.isValid)
|
||||
case SignatureValidationFailureIncorrectSignatures =>
|
||||
ScriptProgramFactory.factory(program, ScriptFalse :: restOfStack,program.script.tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,11 +150,13 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
*/
|
||||
def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIG, "Script top must be OP_CHECKMULTISIG")
|
||||
require(program.stack.size > 2, "Stack must contain at least 3 items for OP_CHECKMULTISIG")
|
||||
|
||||
if (program.flags.contains(ScriptVerifyNullDummy) && program.txSignatureComponent.scriptSignature.asm.head != OP_0) {
|
||||
logger.warn("Script flag null dummy was set however the first element in the script signature was not an OP_0")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else if (program.stack.size < 3) {
|
||||
logger.error("OP_CHECKMULTISIG requires at least 3 stack elements")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
} else {
|
||||
//these next lines remove the appropriate stack/script values after the signatures have been checked
|
||||
val nPossibleSignatures : Int = program.stack.head match {
|
||||
|
|
|
@ -67,9 +67,14 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
*/
|
||||
def opToAltStack(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_TOALTSTACK, "Top of script stack must be OP_TOALTSTACK")
|
||||
require(program.stack.size > 0,"Stack must have at least one item on it for OP_TOALTSTACK")
|
||||
ScriptProgramFactory.factory(program, program.stack.tail,
|
||||
program.script.tail, program.stack.head :: program.altStack, ScriptProgramFactory.AltStack)
|
||||
program.stack.size > 0 match {
|
||||
case true => ScriptProgramFactory.factory(program, program.stack.tail,
|
||||
program.script.tail, program.stack.head :: program.altStack, ScriptProgramFactory.AltStack)
|
||||
case false =>
|
||||
logger.error("OP_TOALTSTACK requires an element to be on the stack")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,11 +217,15 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
*/
|
||||
def op2Rot(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2ROT, "Top of script stack must be OP_2ROT")
|
||||
val newStack = program.stack match {
|
||||
case h :: h1 :: h2 :: h3 :: h4 :: h5 :: t => h4 :: h5 :: h :: h1 :: h2 :: h3 :: t
|
||||
case _ => throw new IllegalArgumentException("Stack must have at least 5 items on it for OP_2ROT")
|
||||
program.stack match {
|
||||
case h :: h1 :: h2 :: h3 :: h4 :: h5 :: t =>
|
||||
val newStack = h4 :: h5 :: h :: h1 :: h2 :: h3 :: t
|
||||
ScriptProgramFactory.factory(program, newStack,program.script.tail)
|
||||
case _ =>
|
||||
logger.error("OP_2ROT requires 6 elements on the stack")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
ScriptProgramFactory.factory(program, newStack,program.script.tail)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -226,8 +235,14 @@ trait StackInterpreter extends BitcoinSLogger {
|
|||
*/
|
||||
def op2Drop(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_2DROP, "Top of script stack must be OP_2DROP")
|
||||
require(program.stack.size > 1,"Stack must have at least 2 items on it for OP_2DROP")
|
||||
ScriptProgramFactory.factory(program, program.stack.tail.tail, program.script.tail)
|
||||
program.stack.size > 1 match {
|
||||
case true =>
|
||||
ScriptProgramFactory.factory(program, program.stack.tail.tail, program.script.tail)
|
||||
case false =>
|
||||
logger.error("OP_2DROP requires two elements to be on the stack")
|
||||
ScriptProgramFactory.factory(program,false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,13 +30,14 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
it must "throw an exception if we have an OP_1ADD with nothing on the stack" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val stack = List()
|
||||
val script = List(OP_1ADD)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op1Add(program)
|
||||
}
|
||||
it must "mark the script as invalid if we have an OP_1ADD with nothing on the stack" in {
|
||||
|
||||
val stack = List()
|
||||
val script = List(OP_1ADD)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op1Add(program)
|
||||
newProgram.isValid must be (false)
|
||||
|
||||
}
|
||||
|
||||
it must "perform an OP_1SUB corectly" in {
|
||||
|
@ -49,13 +50,13 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
it must "throw an exception if we have an OP_1SUB with nothing on the stack" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val stack = List()
|
||||
val script = List(OP_1SUB)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op1Sub(program)
|
||||
}
|
||||
it must "mark a script as invalid if we have an OP_1SUB with nothing on the stack" in {
|
||||
|
||||
val stack = List()
|
||||
val script = List(OP_1SUB)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = op1Sub(program)
|
||||
newProgram.isValid must be (false)
|
||||
}
|
||||
|
||||
it must "perform an OP_SUB corectly" in {
|
||||
|
@ -68,13 +69,12 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
|
||||
it must "throw an exception if we have an OP_SUB with nothing on the stack" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val stack = List()
|
||||
val script = List(OP_SUB)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opSub(program)
|
||||
}
|
||||
it must "mark a script as invalid if we have an OP_SUB with nothing on the stack" in {
|
||||
val stack = List()
|
||||
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 {
|
||||
|
@ -96,13 +96,12 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram.stack.head must be (ScriptNumberFactory.zero)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
it must "throw an exception if we have an OP_ABS with nothing on the stack" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val stack = List()
|
||||
val script = List(OP_ABS)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opAbs(program)
|
||||
}
|
||||
it must "mark a script as invalid if we have an OP_ABS with nothing on the stack" in {
|
||||
val stack = List()
|
||||
val script = List(OP_ABS)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opAbs(program)
|
||||
newProgram.isValid must be (false)
|
||||
}
|
||||
|
||||
it must "perform an OP_NEGATE on a zero correctly" in {
|
||||
|
@ -134,13 +133,13 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
|
|||
newProgram.stack.head must be (ScriptNumberFactory.one)
|
||||
newProgram.script.isEmpty must be (true)
|
||||
}
|
||||
it must "throw an exception if we have an OP_NEGATE with nothing on the stack" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val stack = List()
|
||||
val script = List(OP_NEGATE)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNegate(program)
|
||||
}
|
||||
it must "mark a script as invalid if we have an OP_NEGATE with nothing on the stack" in {
|
||||
|
||||
val stack = List()
|
||||
val script = List(OP_NEGATE)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val newProgram = opNegate(program)
|
||||
newProgram.isValid must be (false)
|
||||
}
|
||||
|
||||
it must "perform an OP_NOT correctly where 0 is the stack top" in {
|
||||
|
|
|
@ -42,13 +42,14 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
|||
result.isValid must be (false)
|
||||
}
|
||||
|
||||
it must "fail for OP_VERIFY when there is nothing on the stack" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
val stack = List()
|
||||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val result = opVerify(program)
|
||||
}
|
||||
it must "mark the script as invalid for OP_VERIFY when there is nothing on the stack" in {
|
||||
|
||||
val stack = List()
|
||||
val script = List(OP_VERIFY)
|
||||
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
|
||||
val result = opVerify(program)
|
||||
result.isValid must be (false)
|
||||
|
||||
}
|
||||
|
||||
it must "fail for verify when there is nothing on the script stack" in {
|
||||
|
|
Loading…
Add table
Reference in a new issue