Implementing OP_TUCK, OP_2DROP, OP_2DUP, OP_3DUP, OP_2OVER, OP_2ROT, OP_2SWAP script operations

This commit is contained in:
Chris Stewart 2016-02-04 09:48:35 -06:00
parent b16b92f057
commit 372ec28a16
3 changed files with 150 additions and 0 deletions

View File

@ -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))

View File

@ -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)
}
}

View File

@ -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)
}
}