Adding ArithemeticInterpreter, implementing OP_ADD

This commit is contained in:
Chris Stewart 2016-01-25 14:04:41 -06:00
parent d79c92cf59
commit 355974b853
6 changed files with 126 additions and 4 deletions

View File

@ -49,7 +49,10 @@ trait ScriptParser extends ScalacoinUtil {
val op = ScriptOperationFactory.fromString(h).get
val parsingHelper : ParsingHelper[String] = parseOperationString(op,accum,t)
loop(parsingHelper.tail,parsingHelper.accum)
case h :: t if (tryParsingInt(h)) =>
//convert the string to int, then convert to hex
val int = h.toInt
loop(t, ScriptConstantImpl(int.toHexString) :: accum)
case h :: t => loop(t, ScriptConstantImpl(h) :: accum)
case Nil => accum
}
@ -176,6 +179,14 @@ trait ScriptParser extends ScalacoinUtil {
ParsingHelper(tail,op :: accum)
}
}
/**
* Checks if a string can be cast to an int
* @param str
* @return
*/
private def tryParsingInt(str : String) = try { str.toInt; true} catch { case _ : Throwable => false}
}
object ScriptParser extends ScriptParser

View File

@ -0,0 +1,49 @@
package org.scalacoin.script.arithmetic
import org.scalacoin.script.constant.{ScriptNumber, ScriptNumberImpl, ScriptConstantImpl, ScriptToken}
/**
* Created by chris on 1/25/16.
*/
trait ArithmeticInterpreter {
/**
* a is added to b.
* @param stack
* @param script
* @return
*/
def opAdd(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
require(script.headOption.isDefined && script.head == OP_ADD, "Script top must be OP_ADD")
require(stack.size > 1, "Stack size must be 2 or more perform an OP_ADD")
val b : Int = intFromScriptToken(stack.head)
val a : Int = intFromScriptToken(stack(1))
val result = numberToScriptToken(a + b)
(result :: stack.slice(2,stack.size), script.tail)
}
/**
* Wraps a scala number into a script token for the script language
* @param num
* @return
*/
private def numberToScriptToken(num : Int) : ScriptToken = {
if (num < 76) ScriptNumberImpl(num)
else ScriptConstantImpl(num.toHexString)
}
/**
* Converts a script token to an integer
* @param token
* @return
*/
private def intFromScriptToken(token : ScriptToken) : Int = token match {
case x : ScriptNumber => x.opCode
case x : ScriptConstantImpl => Integer.parseInt(x.hex,16)
}
}

View File

@ -1,6 +1,7 @@
package org.scalacoin.script.interpreter
import org.scalacoin.protocol.script.{ScriptSignature, ScriptPubKey}
import org.scalacoin.script.arithmetic.{ArithmeticInterpreter, OP_ADD}
import org.scalacoin.script.bitwise.{OP_EQUAL, BitwiseInterpreter, OP_EQUALVERIFY}
import org.scalacoin.script.constant._
import org.scalacoin.script.control.{OP_NOTIF, OP_IF, ControlOperationsInterpreter}
@ -14,7 +15,7 @@ import scala.annotation.tailrec
* Created by chris on 1/6/16.
*/
trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with ControlOperationsInterpreter
with BitwiseInterpreter with ConstantInterpreter {
with BitwiseInterpreter with ConstantInterpreter with ArithmeticInterpreter {
private def logger = LoggerFactory.getLogger(this.getClass().toString)
/**
@ -39,6 +40,8 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case OP_DUP :: t => loop(opDup(stack,script))
case OP_DEPTH :: t => loop(opDepth(stack,script))
//arithmetic operaetions
case OP_ADD :: t => loop(opAdd(stack,script))
//bitwise operations
case OP_EQUAL :: t => {
val (newStack,newScript) = equal(stack, script)

View File

@ -1,7 +1,8 @@
package org.scalacoin.marshallers.script
import org.scalacoin.script.arithmetic.OP_ADD
import org.scalacoin.script.bitwise.OP_EQUAL
import org.scalacoin.script.constant.ScriptConstantImpl
import org.scalacoin.script.constant.{OP_1NEGATE, ScriptConstantImpl}
import org.scalacoin.util.{ScalacoinUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
@ -51,6 +52,12 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
parse(str) must equal (List(ScriptConstantImpl("0100")))
}
it must "parse a script signature with a decimal constant in it" in {
val str = "0x4f 1000 ADD"
//0x3e8 == 1000
parse(str) must equal (List(OP_1NEGATE, ScriptConstantImpl("3e8"), OP_ADD))
}
}

View File

@ -60,4 +60,10 @@ class ScriptOperationFactoryTest extends FlatSpec with MustMatchers {
spliceOperation.get must be (OP_SUBSTR)
}
it must "find OP_1NEGATE from its hex representation" in {
val negateOperation = ScriptOperationFactory.fromHex("4f")
negateOperation.isDefined must be (true)
negateOperation.get must be (OP_1NEGATE)
}
}

View File

@ -0,0 +1,46 @@
package org.scalacoin.script.arithmetic
import org.scalacoin.script.constant.{ScriptConstantImpl, ScriptNumberImpl}
import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 1/25/16.
*/
class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with ArithmeticInterpreter {
"ArithmeticInterpreter" must "perform an OP_ADD correctly" in {
val stack = List(ScriptNumberImpl(1), ScriptNumberImpl(2))
val script = List(OP_ADD)
val (newStack,newScript) = opAdd(stack,script)
newStack.head must be (ScriptNumberImpl(3))
newScript.isEmpty must be (true)
}
it must "perform an OP_ADD correctly on ScriptConstantImpl" in {
//0x64 is the hexadecimal representation for 100
val stack = List(ScriptConstantImpl("64"), ScriptConstantImpl("64"))
val script = List(OP_ADD)
val (newStack,newScript) = opAdd(stack,script)
//0xC8 is 200 in hex
newStack.head must be (ScriptConstantImpl("c8"))
newScript.isEmpty must be (true)
}
it must "perform an OP_ADD correctly on a ScriptConstant & ScriptNumber that are used as the args" in {
val stack = List(ScriptNumberImpl(1), ScriptConstantImpl("64"))
val script = List(OP_ADD)
val (newStack,newScript) = opAdd(stack,script)
//0x65 is 101 in hex
newStack.head must be (ScriptConstantImpl("65"))
newScript.isEmpty must be (true)
}
it must "perform an OP_ADD correctly on a a negative number" in {
val stack = List(ScriptConstantImpl("3e8"), ScriptNumberImpl(-1))
val script = List(OP_ADD)
val (newStack,newScript) = opAdd(stack,script)
newStack.head must be (ScriptConstantImpl("3e7"))
newScript.isEmpty must be (true)
}
}