implementing OP_1ADD, OP_1SUB, OP_SUB and OP_ABS script operations

This commit is contained in:
Chris Stewart 2016-02-06 14:45:36 -06:00
parent 24b79f88b7
commit 53d4ff5e8b
4 changed files with 126 additions and 9 deletions

View file

@ -26,6 +26,76 @@ trait ArithmeticInterpreter {
program.script.tail, program.transaction, program.altStack)
}
/**
* Increments the stack top by 1
* @param program
* @return
*/
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, "Stack size must be 1 or more perform an OP_1ADD")
val newStackTop = program.stack.head match {
case s : ScriptNumber => s + ScriptNumberImpl(1)
case x => throw new RuntimeException("Stack must be script number to perform OP_1ADD, stack top was: " + x)
}
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Decrements the stack top by 1
* @param program
* @return
*/
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 - ScriptNumberImpl(1)
case x => throw new RuntimeException("Stack must be script number to perform OP_1ADD, stack top was: " + x)
}
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* b is subtracted from a.
* @param program
* @return
*/
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 size must be 2 or more perform an OP_SUB")
val b = program.stack.head match {
case s : ScriptNumber => s - ScriptNumberImpl(1)
case x => throw new RuntimeException("Stack must be script number to perform OP_SUB, stack top was: " + x)
}
val a = program.stack.tail.head match {
case s : ScriptNumber => s - ScriptNumberImpl(1)
case x => throw new RuntimeException("Stack must be script number to perform OP_SUB, stack top was: " + x)
}
val newScriptNumber = a - b
ScriptProgramImpl(newScriptNumber :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
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 => ScriptNumberImpl(s.num.abs)
case x => throw new RuntimeException("Stack must be script number to perform OP_ABS, stack top was: " + x)
}
ScriptProgramImpl(newStackTop :: program.stack.tail,
program.script.tail, program.transaction, program.altStack)
}
/**
* Wraps a scala number into a script token for the script language

View file

@ -4,7 +4,7 @@ import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
import org.scalacoin.protocol.transaction.Transaction
import org.scalacoin.script.splice.{SpliceInterpreter, OP_SIZE}
import org.scalacoin.script.{ScriptProgramImpl, ScriptProgram}
import org.scalacoin.script.arithmetic.{ArithmeticInterpreter, OP_ADD}
import org.scalacoin.script.arithmetic._
import org.scalacoin.script.bitwise.{OP_EQUAL, BitwiseInterpreter, OP_EQUALVERIFY}
import org.scalacoin.script.constant._
import org.scalacoin.script.control._
@ -61,8 +61,13 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case OP_3DUP :: t => loop(op3Dup(program))
case OP_2OVER :: t => loop(op2Over(program))
case OP_2SWAP :: t => loop(op2Swap(program))
//arithmetic operations
case OP_ADD :: t => loop(opAdd(program))
case OP_1ADD :: t => loop(op1Add(program))
case OP_1SUB :: t => loop(op1Sub(program))
case OP_SUB :: t => loop(opSub(program))
case OP_ABS :: t => loop(opAbs(program))
//bitwise operations
case OP_EQUAL :: t => {

View file

@ -49,4 +49,54 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
newProgram.stack.head must be (ScriptNumberImpl(999))
newProgram.script.isEmpty must be (true)
}
it must "perform an OP_1ADD correctly" in {
val stack = List(ScriptNumberImpl(0))
val script = List(OP_1ADD)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = op1Add(program)
newProgram.stack.head must be (ScriptNumberImpl(1))
newProgram.script.isEmpty must be (true)
}
it must "perform an OP_1SUB corectly" in {
val stack = List(ScriptNumberImpl(0))
val script = List(OP_1SUB)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = op1Sub(program)
newProgram.stack.head must be (ScriptNumberImpl(-1))
newProgram.script.isEmpty must be (true)
}
it must "perform an OP_SUB corectly" in {
val stack = List(ScriptNumberImpl(1),ScriptNumberImpl(0))
val script = List(OP_SUB)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opSub(program)
newProgram.stack.head must be (ScriptNumberImpl(-1))
newProgram.script.isEmpty must be (true)
}
it must "perform an OP_ABS on a negative number corectly" in {
val stack = List(ScriptNumberImpl(-1))
val script = List(OP_ABS)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opAbs(program)
newProgram.stack.head must be (ScriptNumberImpl(1))
newProgram.script.isEmpty must be (true)
}
it must "perform OP_ABS on zero correctly" in {
val stack = List(ScriptNumberImpl(0))
val script = List(OP_ABS)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opAbs(program)
newProgram.stack.head must be (ScriptNumberImpl(0))
newProgram.script.isEmpty must be (true)
}
}

View file

@ -30,14 +30,6 @@ class SpliceInterpreterTest extends FlatSpec with MustMatchers with SpliceInterp
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_SIZE correctly with something of size 2 bytes" in {
val stack = List(ScriptConstantImpl("80"))
val script = List(OP_SIZE)
val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List())
val newProgram = opSize(program)
newProgram.stack must be (List(ScriptNumberImpl(2),ScriptConstantImpl("80")))
newProgram.script.isEmpty must be (true)
}
it must "evaluate an OP_SIZE correctly with a negative number" in {
val stack = List(ScriptNumberImpl(-1))