From 4aeb30ca6de4965e516fff01e436c8b98e809c79 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Wed, 3 Feb 2016 19:25:02 -0600 Subject: [PATCH] Implementing OP_PICK script operation --- .../interpreter/ScriptInterpreter.scala | 2 ++ .../script/stack/StackInterpreter.scala | 36 ++++++++++++++++++- .../marshallers/script/ScriptParserTest.scala | 9 ++--- .../script/stack/StackInterpreterTest.scala | 21 +++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala index d011eda824..ec32989007 100644 --- a/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/interpreter/ScriptInterpreter.scala @@ -48,6 +48,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con case OP_DROP :: t => loop(opDrop(program)) case OP_IFDUP :: t => loop(opIfDup(program)) case OP_NIP :: t => loop(opNip(program)) + case OP_OVER :: t => loop(opOver(program)) + case OP_PICK :: t => loop(opPick(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 f31185d306..4ff6640dee 100644 --- a/src/main/scala/org/scalacoin/script/stack/StackInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/stack/StackInterpreter.scala @@ -2,6 +2,7 @@ package org.scalacoin.script.stack import org.scalacoin.script.{ScriptProgramImpl, ScriptProgram} import org.scalacoin.script.constant._ +import org.scalacoin.util.ScalacoinUtil /** * Created by chris on 1/6/16. @@ -99,8 +100,41 @@ trait StackInterpreter { require(program.script.headOption.isDefined && program.script.head == OP_NIP, "Top of script stack must be OP_NIP") require(program.stack.size > 1,"Stack must have at least two items on it for OP_NIP") program.stack match { - case h :: h1 :: t => ScriptProgramImpl(h :: t, program.script.tail, program.transaction, program.altStack) + case h :: _ :: t => ScriptProgramImpl(h :: t, program.script.tail, program.transaction, program.altStack) + case h :: t => throw new RuntimeException("Stack must have at least two items on it for OP_NIP") + case Nil => throw new RuntimeException("Stack must have at least two items on it for OP_NIP") } } + + /** + * Copies the second-to-top stack item to the top. + * @param program + * @return + */ + def opOver(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_OVER, "Top of script stack must be OP_OVER") + require(program.stack.size > 1,"Stack must have at least two items on it for OP_OVER") + program.stack match { + case _ :: h1 :: _ => ScriptProgramImpl(h1 :: program.stack, program.script.tail, program.transaction, program.altStack) + case h :: t => throw new RuntimeException("Stack must have at least two items on it for OP_OVER") + case Nil => throw new RuntimeException("Stack must have at least two items on it for OP_OVER") + } + } + + /** + * The item n back in the stack is copied to the top. + * @param program + * @return + */ + def opPick(program : ScriptProgram) : ScriptProgram = { + require(program.script.headOption.isDefined && program.script.head == OP_PICK, "Top of script stack must be OP_PICK") + require(program.stack.size > 1,"Stack must have at least two items on it for OP_PICK") + + val n = ScalacoinUtil.hexToInt(program.stack.head.hex) + val newStackTop = program.stack.tail(n) + ScriptProgramImpl(newStackTop :: program.stack.tail, program.script.tail, program.transaction, program.altStack) + + } + } diff --git a/src/test/scala/org/scalacoin/marshallers/script/ScriptParserTest.scala b/src/test/scala/org/scalacoin/marshallers/script/ScriptParserTest.scala index 79f2deb5dc..85c5d01465 100644 --- a/src/test/scala/org/scalacoin/marshallers/script/ScriptParserTest.scala +++ b/src/test/scala/org/scalacoin/marshallers/script/ScriptParserTest.scala @@ -3,6 +3,7 @@ package org.scalacoin.marshallers.script import org.scalacoin.script.arithmetic.OP_ADD import org.scalacoin.script.bitwise.OP_EQUAL import org.scalacoin.script.constant.{ScriptNumberImpl, OP_1, OP_1NEGATE, ScriptConstantImpl} +import org.scalacoin.script.stack.OP_PICK import org.scalacoin.util.{ScalacoinUtil, TestUtil} import org.scalatest.{FlatSpec, MustMatchers} @@ -58,10 +59,10 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with parse(str) must equal (List(OP_1NEGATE, ScriptConstantImpl("3e8"), OP_ADD)) } -/* it must "parse a decimal number as a ScriptNumber if it is under 75" in { - val str = "ADD 21 EQUAL" - parse(str) must equal (List(OP_ADD, ScriptNumberImpl(21), OP_EQUAL)) - }*/ + it must "parse an OP_PICK" in { + val str = "PICK" + parse(str) must equal (List(OP_PICK)) + } diff --git a/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala b/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala index 4c2c8a3c10..f68e8dc43a 100644 --- a/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala +++ b/src/test/scala/org/scalacoin/script/stack/StackInterpreterTest.scala @@ -1,6 +1,7 @@ package org.scalacoin.script.stack import org.scalacoin.script.ScriptProgramImpl +import org.scalacoin.script.bitwise.OP_EQUAL import org.scalacoin.script.constant.{OP_1, OP_0, ScriptConstantImpl} import org.scalacoin.util.{TestUtil, ScalacoinUtil} import org.scalatest.{FlatSpec, MustMatchers} @@ -106,6 +107,26 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre newProgram.stack must be (List(OP_0)) newProgram.script.isEmpty must be (true) + } + + it must "evaluate an OP_OVER correctly" in { + val stack = List(OP_0,OP_1) + val script = List(OP_OVER) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List()) + val newProgram = opOver(program) + newProgram.stack must be (List(OP_1,OP_0,OP_1)) + newProgram.script.isEmpty must be (true) + } + + it must "evaluate an OP_PICK correctly" in { + val stack = List(OP_0, ScriptConstantImpl("14"), ScriptConstantImpl("15"), ScriptConstantImpl("16")) + val script = List(OP_PICK) + val program = ScriptProgramImpl(stack,script,TestUtil.transaction,List()) + val newProgram = opPick(program) + + newProgram.stack must be (List(ScriptConstantImpl("14"),ScriptConstantImpl("14"), + ScriptConstantImpl("15"), ScriptConstantImpl("16"))) + newProgram.script.isEmpty must be (true) } }