Implementing OP_LESSTHAN, OP_GREATERTHAN, OP_LESSTHANOREQUAL, OP_GREATERTHANOREQUAL script operations

This commit is contained in:
Chris Stewart 2016-02-06 17:39:42 -06:00
parent 6d95a38a03
commit e6f5368e5b
6 changed files with 259 additions and 4 deletions

View file

@ -1,12 +1,13 @@
package org.scalacoin.script.arithmetic
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
import org.scalacoin.script.{ScriptProgramImpl, ScriptProgram}
import org.scalacoin.script.constant._
/**
* Created by chris on 1/25/16.
*/
trait ArithmeticInterpreter {
trait ArithmeticInterpreter extends ControlOperationsInterpreter {
/**
@ -185,6 +186,165 @@ trait ArithmeticInterpreter {
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Returns 1 if the numbers are equal, 0 otherwise.
* @param program
* @return
*/
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 : ScriptNumberOperation, y : ScriptNumber) => x.scriptNumber == y
case (x : ScriptNumber, y : ScriptNumberOperation) => x == y.scriptNumber
case (x,y) => x == y
}
val newStackTop = if (isSame) OP_1 else OP_0
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
* @param program
* @return
*/
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 = ScriptProgramImpl(program.stack, OP_NUMEQUAL :: program.script.tail, program.transaction, program.altStack)
val numEqualResult = opNumEqual(numEqualProgram)
val verifyProgram = ScriptProgramImpl(program.stack, OP_VERIFY :: numEqualResult.script,
numEqualResult.transaction, numEqualResult.altStack)
val verifyResult = opVerify(verifyProgram)
verifyResult
}
/**
* Returns 1 if the numbers are not equal, 0 otherwise.
* @param program
* @return
*/
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")
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
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Returns 1 if a is less than b, 0 otherwise.
* @param program
* @return
*/
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")
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
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Returns 1 if a is greater than b, 0 otherwise.
* @param program
* @return
*/
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")
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
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Returns 1 if a is less than or equal to b, 0 otherwise.
* @param program
* @return
*/
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")
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
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Returns 1 if a is greater than or equal to b, 0 otherwise.
* @param program
* @return
*/
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")
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
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Wraps a scala number into a script token for the script language
* @param num

View file

@ -31,6 +31,11 @@ sealed trait ScriptNumber extends ScriptConstant {
def + (that : ScriptNumber) : ScriptNumber = ScriptNumberImpl(num + that.num)
def - (that : ScriptNumber) : ScriptNumber = ScriptNumberImpl(num - that.num)
def * (that : ScriptNumber) : ScriptNumber = ScriptNumberImpl(num * that.num)
def < (that : ScriptNumber) : Boolean = num < that.num
def <= (that : ScriptNumber) : Boolean = num <= that.num
def > (that : ScriptNumber) : Boolean = num > that.num
def >= (that : ScriptNumber) : Boolean = num >= that.num
}
case class ScriptNumberImpl(num : Long) extends ScriptNumber

View file

@ -73,7 +73,13 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case OP_0NOTEQUAL :: t => loop(op0NotEqual(program))
case OP_BOOLAND :: t => loop(opBoolAnd(program))
case OP_BOOLOR :: t => loop(opBoolOr(program))
case OP_NUMEQUAL :: t => loop(opNumEqual(program))
case OP_NUMEQUALVERIFY :: t => loop(opNumEqualVerify(program))
case OP_NUMNOTEQUAL :: t => loop(opNumNotEqual(program))
case OP_LESSTHAN :: t => loop(opLessThan(program))
case OP_GREATERTHAN :: t => loop(opGreaterThan(program))
case OP_LESSTHANOREQUAL :: t => loop(opLessThanOrEqual(program))
case OP_GREATERTHANOREQUAL :: t => loop(opGreaterThanOrEqual(program))
//bitwise operations
case OP_EQUAL :: t => {
val newProgram = opEqual(program)

View file

@ -21,7 +21,8 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
it must "parse a number larger than an integer into a ScriptNumberImpl" in {
parse("2147483648") must be (List(ScriptNumberImpl(2147483648L)))
}
/* it must "parse a pay-to-pubkey-hash output script" in {
it must "parse a pay-to-pubkey-hash output script" in {
val parsedOutput = parse(TestUtil.p2pkhOutputScriptNotParsedAsm)
parsedOutput must be (TestUtil.p2pkhOutputScriptAsm)
}
@ -70,7 +71,7 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
it must "parse an OP_PICK" in {
val str = "PICK"
parse(str) must equal (List(OP_PICK))
}*/
}

View file

@ -238,5 +238,85 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_NUMEQUAL correctly" in {
val stack = List(OP_0, ScriptNumberImpl(0))
val script = List(OP_NUMEQUAL)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opNumEqual(program)
newProgram.stack.head must be (OP_1)
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_NUMNOTEQUAL for two numbers that are the same" in {
val stack = List(OP_0, ScriptNumberImpl(0))
val script = List(OP_NUMNOTEQUAL)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opNumNotEqual(program)
newProgram.stack.head must be (OP_0)
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_NUMNOTEQUAL for two numbers that are not the same" in {
val stack = List(OP_0, ScriptNumberImpl(1))
val script = List(OP_NUMNOTEQUAL)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opNumNotEqual(program)
newProgram.stack.head must be (OP_1)
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_LESSTHAN correctly" in {
val stack = List(OP_0, ScriptNumberImpl(1))
val script = List(OP_LESSTHAN)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opLessThan(program)
newProgram.stack.head must be (OP_0)
newProgram.script.isEmpty must be (true)
val stack1 = List(OP_0, ScriptNumberImpl(0))
val script1 = List(OP_LESSTHAN)
val program1 = ScriptProgramImpl(stack1,script1,TestUtil.transaction,List())
val newProgram1 = opLessThan(program1)
newProgram1.stack.head must be (OP_0)
newProgram1.script.isEmpty must be (true)
val stack2 = List(OP_1, ScriptNumberImpl(0))
val script2 = List(OP_LESSTHAN)
val program2 = ScriptProgramImpl(stack2,script2,TestUtil.transaction,List())
val newProgram2 = opLessThan(program2)
newProgram2.stack.head must be (OP_1)
newProgram2.script.isEmpty must be (true)
}
it must "evaluate an OP_GREATERTHAN correctly" in {
val stack = List(OP_0, ScriptNumberImpl(1))
val script = List(OP_GREATERTHAN)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opGreaterThan(program)
newProgram.stack.head must be (OP_1)
newProgram.script.isEmpty must be (true)
val stack1 = List(OP_0, ScriptNumberImpl(0))
val script1 = List(OP_GREATERTHAN)
val program1 = ScriptProgramImpl(stack1,script1,TestUtil.transaction,List())
val newProgram1 = opGreaterThan(program1)
newProgram1.stack.head must be (OP_0)
newProgram1.script.isEmpty must be (true)
val stack2 = List(OP_1, ScriptNumberImpl(0))
val script2 = List(OP_GREATERTHAN)
val program2 = ScriptProgramImpl(stack2,script2,TestUtil.transaction,List())
val newProgram2 = opGreaterThan(program2)
newProgram2.stack.head must be (OP_0)
newProgram2.script.isEmpty must be (true)
}
}

View file

@ -1,6 +1,7 @@
package org.scalacoin.script.bitwise
import org.scalacoin.script.ScriptProgramImpl
import org.scalacoin.script.arithmetic.OP_NUMEQUAL
import org.scalacoin.script.constant._
import org.scalacoin.util.TestUtil
import org.scalatest.{MustMatchers, FlatSpec}
@ -100,4 +101,6 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte
opEqual(program).stack.head must be (ScriptTrue)
}
}