Modifying stack interpreter operations to mark the script program as false instead of throwing exceptions

This commit is contained in:
Chris Stewart 2016-04-09 09:29:04 -05:00
parent 737d7369b8
commit 27226ae0e7
3 changed files with 62 additions and 44 deletions

View file

@ -2,14 +2,14 @@ package org.scalacoin.script.stack
import org.scalacoin.script.{ScriptProgramFactory, ScriptProgram}
import org.scalacoin.script.constant._
import org.scalacoin.util.{BitcoinSUtil}
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil}
/**
* Created by chris on 1/6/16.
* Stack operations implemented in the script programming language
* https://en.bitcoin.it/wiki/Script#Stack
*/
trait StackInterpreter {
trait StackInterpreter extends BitcoinSLogger {
/**
* Duplicates the element on top of the stack
@ -19,10 +19,11 @@ trait StackInterpreter {
*/
def opDup(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_DUP, "Top of the script stack must be OP_DUP")
require(program.stack.headOption.isDefined, "Cannot duplicate the top element on an empty stack")
program.stack match {
case h :: t => ScriptProgramFactory.factory(program, h :: program.stack, program.script.tail)
case Nil => throw new RuntimeException("Received an empty stack! Cannot duplicate an element on an empty stack")
case Nil =>
logger.error("Cannot duplicate the top element on an empty stack")
ScriptProgramFactory.factory(program,false)
}
}
@ -33,11 +34,16 @@ trait StackInterpreter {
*/
def opIfDup(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_IFDUP, "Top of the script stack must be OP_DUP")
require(program.stack.headOption.isDefined, "Cannot duplicate the top element on an empty stack")
if (program.stack.head == OP_0) {
ScriptProgramFactory.factory(program,program.stack,program.script.tail)
} else ScriptProgramFactory.factory(program, program.stack.head :: program.stack,
program.script.tail)
program.stack.headOption.isDefined match {
case true if (program.stack.head == OP_0) =>
ScriptProgramFactory.factory(program,program.stack,program.script.tail)
case true => ScriptProgramFactory.factory(program, program.stack.head :: program.stack,
program.script.tail)
case false =>
logger.error("Cannot duplicate the top element on an empty stack")
ScriptProgramFactory.factory(program,false)
}
}
/**
@ -73,9 +79,16 @@ trait StackInterpreter {
*/
def opFromAltStack(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_FROMALTSTACK, "Top of script stack must be OP_FROMALTSTACK")
require(program.altStack.size > 0,"Alt Stack must have at least one item on it for OP_FROMALTSTACK")
ScriptProgramFactory.factory(program, program.altStack.head :: program.stack,
program.script.tail, program.altStack.tail, ScriptProgramFactory.AltStack)
program.altStack.size > 0 match {
case true => ScriptProgramFactory.factory(program, program.altStack.head :: program.stack,
program.script.tail, program.altStack.tail, ScriptProgramFactory.AltStack)
case false =>
logger.error("Alt Stack must have at least one item on it for OP_FROMALTSTACK")
ScriptProgramFactory.factory(program,false)
}
}
/**
@ -85,8 +98,13 @@ trait StackInterpreter {
*/
def opDrop(program : ScriptProgram) : ScriptProgram = {
require(program.script.headOption.isDefined && program.script.head == OP_DROP, "Top of script stack must be OP_DROP")
require(program.stack.size > 0,"Stack must have at least one item on it for OP_DROP")
ScriptProgramFactory.factory(program, program.stack.tail,program.script.tail)
program.stack.size > 0 match {
case true => ScriptProgramFactory.factory(program, program.stack.tail,program.script.tail)
case false =>
logger.error("Stack must have at least one item on it for OP_DROP")
ScriptProgramFactory.factory(program,false)
}
}
@ -99,8 +117,12 @@ trait StackInterpreter {
require(program.script.headOption.isDefined && program.script.head == OP_NIP, "Top of script stack must be OP_NIP")
program.stack match {
case h :: _ :: t => ScriptProgramFactory.factory(program, h :: t, program.script.tail)
case h :: t => throw new IllegalArgumentException("Stack must have at least two items on it for OP_NIP")
case Nil => throw new IllegalArgumentException("Stack must have at least two items on it for OP_NIP")
case h :: t =>
logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgramFactory.factory(program,false)
case Nil =>
logger.error("Stack must have at least two items on it for OP_NIP")
ScriptProgramFactory.factory(program,false)
}
}
@ -114,8 +136,11 @@ trait StackInterpreter {
require(program.script.headOption.isDefined && program.script.head == OP_OVER, "Top of script stack must be OP_OVER")
program.stack match {
case _ :: h1 :: _ => ScriptProgramFactory.factory(program, h1 :: program.stack, program.script.tail)
case h :: t => throw new IllegalArgumentException("Stack must have at least two items on it for OP_OVER")
case Nil => throw new IllegalArgumentException("Stack must have at least two items on it for OP_OVER")
case h :: t => logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgramFactory.factory(program,false)
case Nil =>
logger.error("Stack must have at least two items on it for OP_OVER")
ScriptProgramFactory.factory(program,false)
}
}

View file

@ -74,14 +74,14 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
val source = scala.io.Source.fromFile("src/test/scala/org/scalacoin/script/interpreter/script_invalid.json")
//use this to represent a single test case from script_valid.json
val lines =
/* val lines =
"""
|
|[["1 IF 0 ENDIF", "1 ENDIF", "P2SH,STRICTENC"]]
""".stripMargin
""".stripMargin*/
//val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
val json = lines.parseJson
val testCasesOpt : Seq[Option[CoreTestCase]] = json.convertTo[Seq[Option[CoreTestCase]]]
val testCases : Seq[CoreTestCase] = testCasesOpt.flatten

View file

@ -31,14 +31,12 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
}
}
it must "throw an exception when calling opDup without an element on top of the stack" in {
it must "mark the script invalid when calling opDup without an element on top of the stack" in {
val stack = List()
val script = List(OP_DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
opDup(program).isValid must be (false)
intercept[IllegalArgumentException] {
val stack = List()
val script = List(OP_DUP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
opDup(program)
}
}
it must "evaluate the OP_DEPTH operator correctly" in {
@ -109,15 +107,14 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
newProgram.script.isEmpty must be (true)
}
it must "throw an exception if there is less than 2 elements on the stack for OP_NIP" in {
it must "mark the script as invalid if there is less than 2 elements on the stack for OP_NIP" in {
val stack = List(OP_0)
val script = List(OP_NIP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = opNip(program)
newProgram.isValid must be (false)
intercept[IllegalArgumentException] {
val newProgram = opNip(program)
}
}
it must "throw an exception if there is no elements on the stack for OP_NIP" in {
@ -125,10 +122,8 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
val script = List(OP_NIP)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
intercept[IllegalArgumentException] {
val newProgram = opNip(program)
}
val newProgram = opNip(program)
newProgram.isValid must be (false)
}
it must "evaluate an OP_OVER correctly" in {
@ -140,26 +135,24 @@ class StackInterpreterTest extends FlatSpec with MustMatchers with StackInterpre
newProgram.script.isEmpty must be (true)
}
it must "throw an exception if there is less than 2 elements on the stack for OP_OVER" in {
it must "mark the script as invalid if there is less than 2 elements on the stack for OP_OVER" in {
val stack = List(OP_0)
val script = List(OP_OVER)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = opOver(program)
newProgram.isValid must be (false)
intercept[IllegalArgumentException] {
val newProgram = opOver(program)
}
}
it must "throw an exception if there is no elements on the stack for OP_OVER" in {
it must "mark the script as invalid if there is no elements on the stack for OP_OVER" in {
val stack = List()
val script = List(OP_OVER)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack,script)
val newProgram = opOver(program)
newProgram.isValid must be (false)
intercept[IllegalArgumentException] {
val newProgram = opOver(program)
}
}
it must "evaluate an OP_PICK correctly" in {