mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 09:52:09 +01:00
Parsing fixing bug with parsing nested OP_ELSES into binary tree, removing first OP_ELSE in the tree
This commit is contained in:
parent
171053bf94
commit
4bd236cf9f
@ -32,62 +32,18 @@ trait ControlOperationsInterpreter {
|
||||
* @param script
|
||||
* @return
|
||||
*/
|
||||
/*
|
||||
def opIf(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
|
||||
require(script.headOption.isDefined && script.head == OP_IF, "Script top was not OP_IF")
|
||||
val (opElseIndex,opEndIfIndex) = findFirstIndexesOpElseOpEndIf(script)
|
||||
stack.head match {
|
||||
case OP_0 =>
|
||||
//means that we need to execute the OP_ELSE statement if one exists
|
||||
//need to remove the OP_IF expression from the script
|
||||
//since it should not be executed
|
||||
require(opEndIfIndex.isDefined,"Every OP_IF must have a matching OP_ENDIF statement")
|
||||
//means that we have an else statement which needs to be executed
|
||||
if (opElseIndex.isDefined) {
|
||||
//removes the OP_ELSE as well
|
||||
val newScript = script.slice(opElseIndex.get,script.size)
|
||||
opElse(stack.tail,newScript)
|
||||
} else {
|
||||
//means that we do not have an OP_ELSE statement
|
||||
//removes the OP_ENDIF as well
|
||||
val newScript = script.slice(opEndIfIndex.get+1,script.size)
|
||||
(stack.tail,newScript)
|
||||
}
|
||||
case _ =>
|
||||
//means that we need to execute the OP_IF expression
|
||||
//and delete its corresponding OP_ELSE if one exists
|
||||
if (opElseIndex.isDefined) {
|
||||
logger.debug("OP_ELSE index: " + opElseIndex.get)
|
||||
logger.debug("OP_ENDIF index: " + opEndIfIndex.get)
|
||||
//means we have an OP_ELSE expression that needs to be removed
|
||||
//start at index 1 to remove the OP_IF
|
||||
val scriptPart1 = script.slice(1,opElseIndex.get)
|
||||
|
||||
val scriptWithoutOpElse = script.zipWithIndex.filter(_._2 != opElseIndex.get).map(_._1)
|
||||
|
||||
val newOpElseIndex = findFirstOpElse(scriptWithoutOpElse)
|
||||
|
||||
//means that we have another OP_ELSE before our OP_ENDIF.
|
||||
val scriptPart2 = if (newOpElseIndex.isDefined && newOpElseIndex.get < opEndIfIndex.get) {
|
||||
//the +1 is because we removed the OP_ELSE
|
||||
script.slice(newOpElseIndex.get+1,script.size)
|
||||
} else script.slice(opEndIfIndex.get,script.size)
|
||||
|
||||
val newScript = scriptPart1 ++ scriptPart2
|
||||
(stack.tail,newScript)
|
||||
} else (stack.tail,script.tail)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
def opIf(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
|
||||
require(script.headOption.isDefined && script.head == OP_IF, "Script top was not OP_IF")
|
||||
val binaryTree = parseBinaryTree(script)
|
||||
logger.debug("Binary tree: " + binaryTree)
|
||||
if (stack.head != OP_0) {
|
||||
//remove the OP_ELSE if one exists
|
||||
val newTree : Option[BinaryTree[ScriptToken]] = binaryTree.left
|
||||
if (newTree.isDefined) (stack.tail,newTree.get.toSeq.toList) else (stack.tail,List())
|
||||
//remove OP_ELSE from binary tree
|
||||
val newTreeWithoutOpElse = if (newTree.isDefined) Some(removeFirstOpElse(newTree.get)) else None
|
||||
if (newTreeWithoutOpElse.isDefined) (stack.tail,newTreeWithoutOpElse.get.toSeq.toList) else (stack.tail,List())
|
||||
} else {
|
||||
//remove the OP_IF
|
||||
val scriptWithoutOpIf = removeFirstOpIf(script)
|
||||
@ -164,9 +120,7 @@ trait ControlOperationsInterpreter {
|
||||
case OP_IF :: t =>
|
||||
val lastOpEndIfIndex = findLastOpEndIf(t)
|
||||
val lastOpElseIndex = findLastOpElse(t)
|
||||
|
||||
if (lastOpEndIfIndex.isDefined && !t.contains(OP_IF)) {
|
||||
|
||||
val opElseIndex : Option[Int] = findFirstOpElse(t)
|
||||
val opIfExpression = if (opElseIndex.isDefined) t.slice(0,opElseIndex.get) else t.slice(0, lastOpEndIfIndex.get)
|
||||
val restOfScript = if (opElseIndex.isDefined) t.slice(opElseIndex.get, script.size) else t.slice(lastOpEndIfIndex.get, script.size)
|
||||
@ -180,10 +134,16 @@ trait ControlOperationsInterpreter {
|
||||
} else Node(OP_IF,loop(t),Empty)
|
||||
|
||||
case OP_ELSE :: t =>
|
||||
val lastOpEndIf = findLastOpEndIf(t)
|
||||
if (lastOpEndIf.isDefined) {
|
||||
val opElseExpression = t.slice(0,lastOpEndIf.get)
|
||||
val restOfScript = t.slice(lastOpEndIf.get,t.size)
|
||||
val nestedOpElseIndex = findFirstOpElse(t)
|
||||
val lastOpEndIfIndex = findLastOpEndIf(t)
|
||||
|
||||
if (nestedOpElseIndex.isDefined && lastOpEndIfIndex.isDefined && nestedOpElseIndex.get < lastOpEndIfIndex.get) {
|
||||
val opElseExpression = t.slice(0,nestedOpElseIndex.get)
|
||||
val nestedOpElseExpression = t.slice(nestedOpElseIndex.get,t.size)
|
||||
Node(OP_ELSE, loop(opElseExpression), loop(nestedOpElseExpression))
|
||||
} else if (lastOpEndIfIndex.isDefined) {
|
||||
val opElseExpression = t.slice(0,lastOpEndIfIndex.get)
|
||||
val restOfScript = t.slice(lastOpEndIfIndex.get,t.size)
|
||||
Node(OP_ELSE, loop(opElseExpression), loop(restOfScript))
|
||||
} else Node(OP_ELSE,loop(t),Empty)
|
||||
|
||||
@ -266,7 +226,18 @@ trait ControlOperationsInterpreter {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes the first op else in a binary tree
|
||||
* @param tree
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
def removeFirstOpElse[T](tree : BinaryTree[T]) : BinaryTree[T] = {
|
||||
logger.debug("Binary tree: " + tree)
|
||||
if (tree.right.isDefined && tree.right.get.value == Some(OP_ELSE)) {
|
||||
Node(tree.value.get,tree.left.getOrElse(Empty),tree.right.get.right.getOrElse(Empty))
|
||||
} else tree
|
||||
}
|
||||
/**
|
||||
* Removes the first OP_IF { expression } encountered in the script
|
||||
* @param script
|
||||
|
@ -32,7 +32,6 @@ trait BinaryTree[+T] {
|
||||
* @return
|
||||
*/
|
||||
def toSeqLeafValues : Seq[T] = {
|
||||
|
||||
//TODO: Optimize this into a tailrec function
|
||||
def loop(tree : BinaryTree[T],accum : List[T]) : Seq[T] = tree match {
|
||||
case Leaf(x) => x :: accum
|
||||
|
@ -4,6 +4,7 @@ import org.scalacoin.script.arithmetic.OP_ADD
|
||||
import org.scalacoin.script.bitwise.OP_EQUAL
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.reserved.{OP_VER, OP_RESERVED}
|
||||
import org.scalacoin.util.Leaf
|
||||
import org.scalatest.{MustMatchers, FlatSpec}
|
||||
|
||||
/**
|
||||
@ -84,6 +85,20 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
||||
removeFirstOpElse(List(OP_IF, OP_1,OP_ELSE, OP_2, OP_ELSE, OP_3, OP_ENDIF)) must be (List(OP_IF, OP_1, OP_ELSE, OP_3, OP_ENDIF))
|
||||
}
|
||||
|
||||
it must "remove the first OP_ELSE in a binary tree" in {
|
||||
val script1 = List(OP_IF,OP_ELSE,OP_ENDIF)
|
||||
val bTree1 = parseBinaryTree(script1)
|
||||
removeFirstOpElse(bTree1).toSeq must be (List(OP_IF,OP_ENDIF))
|
||||
|
||||
val script2 = List(OP_IF,OP_ENDIF)
|
||||
val bTree2 = parseBinaryTree(script2)
|
||||
removeFirstOpElse(bTree2).toSeq must be (script2)
|
||||
|
||||
val script3 = List(OP_IF, OP_1,OP_ELSE, OP_2, OP_ELSE, OP_3, OP_ENDIF)
|
||||
val bTree3 = parseBinaryTree(script3)
|
||||
removeFirstOpElse(bTree3).toSeq must be (List(OP_IF, OP_1, OP_ELSE, OP_3, OP_ENDIF))
|
||||
}
|
||||
|
||||
it must "find a matching OP_ENDIF for an OP_IF" in {
|
||||
//https://gist.github.com/Christewart/381dc1dbbb07e62501c3
|
||||
val script = List(OP_IF, OP_1, OP_IF, OP_RETURN, OP_ELSE, OP_RETURN, OP_ELSE, OP_RETURN, OP_ENDIF,
|
||||
@ -110,7 +125,6 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
||||
|
||||
it must "parse a script as a binary tree then convert it back to the original list" in {
|
||||
|
||||
|
||||
val script0 = List(OP_IF,OP_ENDIF)
|
||||
parseBinaryTree(script0).toSeq must be (script0)
|
||||
|
||||
@ -125,8 +139,11 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
||||
val bTree3 = parseBinaryTree(script3)
|
||||
bTree3.toSeq must be (script3)
|
||||
|
||||
val script = List(OP_IF, OP_IF, OP_0, OP_ELSE, OP_1, OP_ENDIF, OP_ELSE, OP_IF, OP_2, OP_ELSE, OP_3, OP_ENDIF, OP_ENDIF)
|
||||
parseBinaryTree(script).toSeq must be (script)
|
||||
val script4 = List(OP_IF, OP_IF, OP_0, OP_ELSE, OP_1, OP_ENDIF, OP_ELSE, OP_IF, OP_2, OP_ELSE, OP_3, OP_ENDIF, OP_ENDIF)
|
||||
parseBinaryTree(script4).toSeq must be (script4)
|
||||
|
||||
val script5 = List(OP_IF, OP_1,OP_ELSE, OP_2, OP_ELSE, OP_3, OP_ENDIF)
|
||||
parseBinaryTree(script5).toSeq must be (script5)
|
||||
}
|
||||
|
||||
it must "parse a script into a binary tree and have the OP_IF expression on the left branch and the OP_ELSE expression on the right branch"in {
|
||||
@ -148,25 +165,57 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
|
||||
bTree.right.get.right.get.value must be (Some(OP_ENDIF))
|
||||
}
|
||||
|
||||
/* it must "evaluate an OP_IF block correctly if the stack top is true" in {
|
||||
it must "parse nested OP_ELSE statements into the same branch" in {
|
||||
val script = List(OP_IF, OP_1,OP_ELSE, OP_2, OP_ELSE, OP_3, OP_ENDIF)
|
||||
val bTree = parseBinaryTree(script)
|
||||
println(bTree)
|
||||
bTree.value.get must be (OP_IF)
|
||||
|
||||
bTree.left.isDefined must be (true)
|
||||
bTree.left.get.value must be (Some(OP_1))
|
||||
|
||||
bTree.right.isDefined must be (true)
|
||||
bTree.right.get.value must be (Some(OP_ELSE))
|
||||
|
||||
bTree.right.get.left.isDefined must be (true)
|
||||
bTree.right.get.left.get.value must be (Some(OP_2))
|
||||
|
||||
bTree.right.get.right.isDefined must be (true)
|
||||
bTree.right.get.right.get.value must be (Some(OP_ELSE))
|
||||
|
||||
bTree.right.get.right.get.left.isDefined must be (true)
|
||||
bTree.right.get.right.get.left.get.value must be (Some(OP_3))
|
||||
|
||||
bTree.right.get.right.get.right.isDefined must be (true)
|
||||
bTree.right.get.right.get.right.get.value must be (Some(OP_ENDIF))
|
||||
|
||||
bTree.right.get.right.get.left.isDefined must be (true)
|
||||
bTree.right.get.right.get.left.get.value must be (Some(OP_3))
|
||||
|
||||
bTree.right.get.right.get.right.isDefined must be (true)
|
||||
bTree.right.get.right.get.right must be (Some(Leaf(OP_ENDIF)))
|
||||
|
||||
}
|
||||
|
||||
/*it must "evaluate an OP_IF block correctly if the stack top is true" in {
|
||||
val stack = List(OP_1)
|
||||
val script = List(OP_IF, OP_1, OP_ELSE, OP_0, OP_ENDIF)
|
||||
val (newStack,newScript) = opIf(stack,script)
|
||||
|
||||
newStack must be (List())
|
||||
newScript must be (List(OP_1, OP_ENDIF))
|
||||
}*/
|
||||
newScript must be (List(OP_1))
|
||||
}
|
||||
|
||||
/*it must "evaluate a weird case using multiple OP_ELSEs" in {
|
||||
it must "evaluate a weird case using multiple OP_ELSEs" in {
|
||||
val stack = List(ScriptNumberImpl(1))
|
||||
val script = List(OP_IF, OP_ELSE, OP_0, OP_ELSE, OP_1, OP_ENDIF)
|
||||
|
||||
val (newStack,newScript) = opIf(stack,script)
|
||||
|
||||
newScript must be (List(OP_ELSE,OP_1,OP_ENDIF))
|
||||
}
|
||||
}*/
|
||||
|
||||
it must "evaluate this OP_IF OP_ELSE block correctly" in {
|
||||
/*it must "evaluate this OP_IF OP_ELSE block correctly" in {
|
||||
//https://gist.github.com/Christewart/381dc1dbbb07e62501c3
|
||||
val stack = List(OP_0)
|
||||
val script = List(OP_IF, OP_1, OP_IF, OP_RETURN, OP_ELSE, OP_RETURN, OP_ELSE, OP_RETURN, OP_ENDIF,
|
||||
|
Loading…
Reference in New Issue
Block a user