From 53d4ff5e8bd8c66c1588b7ab479b67f1ca04491c Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Sat, 6 Feb 2016 14:45:36 -0600 Subject: [PATCH] implementing OP_1ADD, OP_1SUB, OP_SUB and OP_ABS script operations --- .../arithmetic/ArithmeticInterpreter.scala | 70 +++++++++++++++++++ .../interpreter/ScriptInterpreter.scala | 7 +- .../ArithmeticInterpreterTest.scala | 50 +++++++++++++ .../script/splice/SpliceInterpreterTest.scala | 8 --- 4 files changed, 126 insertions(+), 9 deletions(-) diff --git a/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala b/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala index 43b8033dec..c11d5f75bd 100644 --- a/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreter.scala @@ -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 diff --git a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala index a83c9640d9..1c49b16583 100644 --- a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala @@ -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 => { diff --git a/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala b/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala index 4160c9b132..8f2af5037f 100644 --- a/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/arithmetic/ArithmeticInterpreterTest.scala @@ -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) + } } diff --git a/src/test/scala/org/scalacoin/script/splice/SpliceInterpreterTest.scala b/src/test/scala/org/scalacoin/script/splice/SpliceInterpreterTest.scala index 315d0008a6..9e5e39ab7e 100644 --- a/src/test/scala/org/scalacoin/script/splice/SpliceInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/splice/SpliceInterpreterTest.scala @@ -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))