Adding reserved operations

This commit is contained in:
Chris Stewart 2016-01-22 16:25:25 -06:00
parent 60bb8e0e6b
commit 8039a72dfc
7 changed files with 225 additions and 6 deletions

View File

@ -6,6 +6,7 @@ import org.scalacoin.script.constant._
import org.scalacoin.script.control.ControlOperationsFactory
import org.scalacoin.script.crypto.CryptoOperationFactory
import org.scalacoin.script.locktime.LocktimeOperationFactory
import org.scalacoin.script.reserved.ReservedOperationFactory
import org.scalacoin.script.splice.SpliceOperationsFactory
import org.scalacoin.script.stack.StackOperationFactory
import org.scalacoin.util.ScalacoinUtil
@ -70,9 +71,10 @@ trait ScriptOperationFactory[T <: ScriptOperation] extends ScalacoinUtil {
object ScriptOperationFactory extends ScriptOperationFactory[ScriptOperation] {
override def operations = StackOperationFactory.operations ++ LocktimeOperationFactory.operations ++
lazy val operations = StackOperationFactory.operations ++ LocktimeOperationFactory.operations ++
CryptoOperationFactory.operations ++ ControlOperationsFactory.operations ++ BitwiseOperationsFactory.operations ++
ArithmeticOperationsFactory.operations ++ ScriptNumberFactory.operations ++ SpliceOperationsFactory.operations ++
ReservedOperationFactory.operations ++
Seq(OP_0,OP_1,OP_1NEGATE, OP_2,OP_3,OP_4,OP_5,OP_6,OP_7,OP_8,
OP_9,OP_10,OP_11,OP_12,OP_13,OP_14,OP_15,OP_16,OP_FALSE,OP_PUSHDATA1, OP_PUSHDATA2,OP_PUSHDATA4,OP_TRUE)

View File

@ -1,6 +1,6 @@
package org.scalacoin.script.control
import org.scalacoin.script.constant.{ScriptTrue, ScriptConstantImpl, ScriptToken}
import org.scalacoin.script.constant._
/**
* Created by chris on 1/6/16.
@ -19,4 +19,98 @@ trait ControlOperationsInterpreter {
require(script.headOption.isDefined && script.head == OP_VERIFY, "Top of script stack must be OP_VERIFY")
if (stack.head == ScriptTrue) (stack.tail,script.tail,true) else (stack.tail,script.tail,false)
}
/**
* If the top stack value is not 0, the statements are executed. The top stack value is removed.
* @param stack
* @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")
stack.head match {
case OP_0 =>
//need to remove the statements from the script since
//they should not be executed
val indexes = findIndexesOP_ELSE_OP_ENDIF(script.tail)
require(indexes._2.isDefined,"Every OP_IF must have a matching OP_ENDIF statement")
//means that we have an else statement which needs to be executed
if (indexes._1.isDefined) {
//removes the OP_ELSE as well
val newScript = script.slice(0,indexes._1.get+1)
(stack.tail,newScript)
} else {
//means that we do not have an OP_ELSE statement
//removes the OP_ENDIF as well
val newScript = script.slice(0,indexes._2.get+1)
(stack.tail,newScript)
}
case _ => (stack.tail,script.tail)
}
}
/**
* If the top stack value is 0, the statements are executed. The top stack value is removed.
* @param stack
* @param script
* @return
*/
def opNotIf(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
require(script.headOption.isDefined && script.head == OP_NOTIF, "Script top was not OP_NOTIF")
//since OP_NOTIF does the exact opposite of OP_NOTIF, we can just replace the stack/script tops with
//the opposites and get the same functionality
if (stack.head == OP_0) opIf(OP_1 :: stack.tail,OP_IF :: script.tail)
else opIf(OP_0 :: stack.tail, OP_IF :: script.tail)
}
/**
* Evaluates the OP_ENDIF operator
* @param stack
* @param script
* @return
*/
def opEndIf(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
require(script.headOption.isDefined && script.head == OP_ENDIF, "Script top must be OP_ENDIF")
(stack,script.tail)
}
/**
* Returns the first index of an OP_ENDIF
* @param script
* @return
*/
def findOP_ENDIF(script : List[ScriptToken]) : Option[Int] = {
val index = script.indexOf(OP_ENDIF)
index match {
case -1 => None
case _ => Some(index)
}
}
/**
* Returns the first index of an OP_ENDIF
* @param script
* @return
*/
def findOP_ELSE(script : List[ScriptToken]) : Option[Int] = {
val index = script.indexOf(OP_ELSE)
index match {
case -1 => None
case _ => Some(index)
}
}
/**
* Finds the indexes of our OP_ELSE (if it exists) and our OP_ENDIF
* @param script
* @return
*/
def findIndexesOP_ELSE_OP_ENDIF(script : List[ScriptToken]) : (Option[Int],Option[Int]) = {
val indexOP_ELSE = findOP_ELSE(script)
val indexOP_ENDIF = findOP_ENDIF(script)
(indexOP_ELSE,indexOP_ENDIF)
}
}

View File

@ -3,7 +3,7 @@ package org.scalacoin.script.interpreter
import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
import org.scalacoin.script.bitwise.{OP_EQUAL, BitwiseInterpreter, OP_EQUALVERIFY}
import org.scalacoin.script.constant._
import org.scalacoin.script.control.ControlOperationsInterpreter
import org.scalacoin.script.control.{OP_NOTIF, OP_IF, ControlOperationsInterpreter}
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160, CryptoInterpreter}
import org.scalacoin.script.stack.{OP_DEPTH, StackInterpreter, OP_DUP}
import org.slf4j.LoggerFactory
@ -57,7 +57,9 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
//TODO: is this right? I need to just push a constant on the input stack???
case ScriptConstantImpl(x) :: t => loop((ScriptConstantImpl(x) :: stack, t))
//control operations
case OP_IF :: t => loop(opIf(stack,script))
case OP_NOTIF :: t => loop(opNotIf(stack,script))
//crypto operations
case OP_HASH160 :: t => loop(hash160(stack,script))
case OP_CHECKSIG :: t => checkSig(stack,script,fullScript)

View File

@ -0,0 +1,87 @@
package org.scalacoin.script.reserved
import org.scalacoin.script.constant.ScriptOperation
/**
* Created by chris on 1/22/16.
*/
/**
* Reserved words
* Any opcode not assigned is also reserved. Using an unassigned opcode makes the transaction invalid.
* https://en.bitcoin.it/wiki/Script#Reserved_words
*/
sealed trait ReservedOperation extends ScriptOperation
/**
* Transaction is invalid unless occuring in an unexecuted OP_IF branch
*/
case object OP_RESERVED extends ReservedOperation {
override def opCode = 80
}
/**
* Transaction is invalid unless occuring in an unexecuted OP_IF branch
*/
case object OP_VER extends ReservedOperation {
override def opCode = 98
}
/**
* Transaction is invalid even when occuring in an unexecuted OP_IF branch
*/
case object OP_VERIF extends ReservedOperation {
override def opCode = 101
}
/**
* Transaction is invalid even when occuring in an unexecuted OP_IF branch
*/
case object OP_VERNOTIF extends ReservedOperation {
override def opCode = 102
}
/**
* Transaction is invalid unless occuring in an unexecuted OP_IF branch
*/
case object OP_RESERVED1 extends ReservedOperation {
override def opCode = 137
}
/**
* Transaction is invalid unless occuring in an unexecuted OP_IF branch
*/
case object OP_RESERVED2 extends ReservedOperation {
override def opCode = 138
}
case object OP_NOP1 extends ReservedOperation {
override def opCode = 176
}
case object OP_NOP3 extends ReservedOperation {
override def opCode = 178
}
case object OP_NOP4 extends ReservedOperation {
override def opCode = 179
}
case object OP_NOP5 extends ReservedOperation {
override def opCode = 180
}
case object OP_NOP6 extends ReservedOperation {
override def opCode = 181
}
case object OP_NOP7 extends ReservedOperation {
override def opCode = 182
}
case object OP_NOP8 extends ReservedOperation {
override def opCode = 183
}
case object OP_NOP9 extends ReservedOperation {
override def opCode = 184
}
case object OP_NOP10 extends ReservedOperation {
override def opCode = 185
}

View File

@ -1,6 +1,6 @@
package org.scalacoin.script.control
import org.scalacoin.script.constant.{ScriptFalse, ScriptTrue, ScriptConstantImpl}
import org.scalacoin.script.constant._
import org.scalatest.{MustMatchers, FlatSpec}
/**
@ -37,4 +37,24 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
val result = verify(stack,script)
}
}
it must "find the indexes of our OP_ENDIF in a list of script tokens" in {
val l = List(OP_ENDIF)
findOP_ENDIF(l) must be (Some(0))
findOP_ENDIF(List(OP_IF,OP_ELSE,OP_ENDIF,OP_ENDIF)) must be (Some(2))
findOP_ENDIF(List(OP_0,OP_1,OP_2)) must be (None)
}
it must "find the indexes of OP_ELSE in a list of script tokens" in {
findOP_ELSE(List(OP_ELSE)) must be (Some(0))
findOP_ELSE(List(OP_IF,OP_ELSE,OP_ENDIF,OP_ELSE)) must be (Some(1))
findOP_ELSE(List(OP_0,OP_1,OP_2)) must be (None)
}
it must "find the indexes of OP_ELSE and OP_ENDIF in a list of script tokens" in {
findIndexesOP_ELSE_OP_ENDIF(List(OP_ELSE,OP_ENDIF)) must be (Some(0),Some(1))
findIndexesOP_ELSE_OP_ENDIF(List(OP_IF, OP_ELSE,OP_ENDIF, OP_IF,OP_ELSE,OP_ENDIF)) must be (Some(1),Some(2))
findIndexesOP_ELSE_OP_ENDIF(List(OP_IF,OP_IF)) must be (None,None)
}
}

View File

@ -42,7 +42,7 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|[["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved (ok if not executed)"]]
""".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

@ -0,0 +1,14 @@
package org.scalacoin.script.reserved
import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 1/22/16.
*/
class ReservedOperationsFactoryTest extends FlatSpec with MustMatchers {
"ReservedOperationsFactory" must "instantiate reserved operations" in {
ReservedOperationFactory.fromHex("50") must be (Some(OP_RESERVED))
ReservedOperationFactory.fromHex("62") must be (Some(OP_VER))
}
}