diff --git a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala index a2ab70fc4e..f35d930435 100644 --- a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala @@ -55,6 +55,11 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con case OP_2ROT :: t => loop(op2Rot(program)) case OP_2DROP :: t => loop(op2Drop(program)) case OP_SWAP :: t => loop(opSwap(program)) + case OP_TUCK :: t => loop(opTuck(program)) + case OP_2DUP :: t => loop(op2Dup(program)) + 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)) diff --git a/src/main/scala/org/scalacoin/script/stack/StackInterpreter.scala b/src/main/scala/org/scalacoin/script/stack/StackInterpreter.scala index 1852c9054e..9a915594ef 100644 --- a/src/main/scala/org/scalacoin/script/stack/StackInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/stack/StackInterpreter.scala @@ -209,4 +209,87 @@ trait StackInterpreter { ScriptProgramImpl(newStack, program.script.tail, program.transaction, program.altStack) } + + /** + * The item at the top of the stack is copied and inserted before the second-to-top item. + * x1 x2 -> x2 x1 x2 + * @param program + * @return + */ + def opTuck(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_TUCK, "Top of script stack must be OP_TUCK") + require(program.stack.size > 1,"Stack must have at least 2 items on it for OP_TUCK") + + val newStack = program.stack match { + case h :: h1 :: t => h1 :: h :: h1 :: t + case _ => throw new RuntimeException("Stack must have at least 2 items on it for OP_TUCK") + } + ScriptProgramImpl(newStack, program.script.tail, program.transaction, program.altStack) + } + + + /** + * Duplicates the top two stack items. + * @param program + * @return + */ + def op2Dup(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_2DUP, "Top of script stack must be OP_2DUP") + require(program.stack.size > 1,"Stack must have at least 2 items on it for OP_2DUP") + + val newStack = program.stack match { + case h :: h1 :: t => h :: h1 :: h :: h1 :: t + case _ => throw new RuntimeException("Stack must have at least 2 items on it for OP_2DUP") + } + ScriptProgramImpl(newStack, program.script.tail, program.transaction, program.altStack) + } + + /** + * Duplicates the top three stack items. + * @param program + * @return + */ + def op3Dup(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_3DUP, "Top of script stack must be OP_3DUP") + require(program.stack.size > 2,"Stack must have at least 3 items on it for OP_3DUP") + val newStack = program.stack match { + case h :: h1 :: h2 :: t => h :: h1 :: h2 :: h :: h1 :: h2 :: t + case _ => throw new RuntimeException("Stack must have at least 3 items on it for OP_3DUP") + } + ScriptProgramImpl(newStack,program.script.tail, program.transaction, program.altStack) + } + + + /** + * Copies the pair of items two spaces back in the stack to the front. + * x1 x2 x3 x4 -> x1 x2 x3 x4 x1 x2 + * @param program + * @return + */ + def op2Over(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_2OVER, "Top of script stack must be OP_2OVER") + require(program.stack.size > 3,"Stack must have at least 4 items on it for OP_2OVER") + val newStack = program.stack match { + case h :: h1 :: h2 :: h3 :: t => h2 :: h3 :: h :: h1 :: h2 :: h3 :: t + case _ => throw new RuntimeException("Stack must have at least 4 items on it for OP_2OVER") + } + ScriptProgramImpl(newStack,program.script.tail, program.transaction, program.altStack) + } + + /** + * Swaps the top two pairs of items. + * @param program + * @return + */ + def op2Swap(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_2SWAP, "Top of script stack must be OP_2SWAP") + require(program.stack.size > 3,"Stack must have at least 4 items on it for OP_2SWAP") + + val newStack = program.stack match { + case h :: h1 :: h2 :: h3 :: t => h2 :: h3 :: h :: h1 :: t + case _ => throw new RuntimeException("Stack must have at least 4 items on it for OP_2SWAP") + } + ScriptProgramImpl(newStack,program.script.tail, program.transaction, program.altStack) + } + } diff --git a/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala b/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala index 440b4a4510..287b3c5779 100644 --- a/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala @@ -185,4 +185,66 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) newProgram.script.isEmpty must be (true) } + + it must "evaluate an OP_TUCK correctly" in { + val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) + val script = List(OP_TUCK) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction, List()) + val newProgram = opTuck(program) + newProgram.stack must be (List(ScriptConstantImpl("15"),ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) + newProgram.script.isEmpty must be (true) + } + + it must "evaluate an OP_2DUP correctly" in { + val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) + val script = List(OP_2DUP) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction, List()) + val newProgram = op2Dup(program) + newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"), + ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) + newProgram.script.isEmpty must be (true) + } + + it must "evaluate an OP_3DUP correctly" in { + val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) + val script = List(OP_3DUP) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction, List()) + val newProgram = op3Dup(program) + + newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("15"),ScriptConstantImpl("16"), + ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) + newProgram.script.isEmpty must be (true) + } + + it must "evaluate an OP_2OVER correctly" in { + val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) + val script = List(OP_2OVER) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction, List()) + val newProgram = op2Over(program) + + newProgram.stack must be (List(ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("14"),ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) + newProgram.script.isEmpty must be (true) + } + + + it must "evaluate an OP_2SWAP correctly" in { + val stack = List(ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16"), + ScriptConstantImpl("17"), ScriptConstantImpl("18"), ScriptConstantImpl("19")) + val script = List(OP_2SWAP) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction, List()) + val newProgram = op2Swap(program) + + newProgram.stack must be (List(ScriptConstantImpl("16"), ScriptConstantImpl("17"),ScriptConstantImpl("14"), + ScriptConstantImpl("15"), ScriptConstantImpl("18"), ScriptConstantImpl("19"))) + newProgram.script.isEmpty must be (true) + } }