implementation done for OP_PUSHDATA1, OP_PUSHDATA2, and OP_PUSHDATA4

This commit is contained in:
Chris Stewart 2016-01-25 12:05:46 -06:00
parent ea517312ab
commit 92bb70b8f8
7 changed files with 32 additions and 26 deletions

View File

@ -34,7 +34,7 @@ trait ScriptParser extends ScalacoinUtil {
val hex = ScalacoinUtil.encodeHex(strippedQuotes.getBytes)
loop(t, ScriptConstantImpl(hex) :: accum)
//if we see a byte constant of just 0x09
//parse the two characters as a hex op
//parse the characters as a hex op
case h :: t if (h.size == 4 && h.substring(0,2) == "0x") =>
val hexString = h.substring(2,h.size)
logger.debug("Hex string: " + hexString)
@ -117,15 +117,12 @@ trait ScriptParser extends ScalacoinUtil {
* @return
*/
private def parseBytesFromString(s: String) : List[ScriptConstant] = {
val hexStrings : List[String] = (raw"\b0x([0-9a-f]+)\b".r
logger.debug("Parsing bytes from string " + s)
val scriptConstants : List[ScriptConstant] = (raw"\b0x([0-9a-f]+)\b".r
.findAllMatchIn(s)
.map(g => BigInt(g.group(1), 16).toString(16))
.map(g => ScriptConstantImpl(g.group(1)))
.toList)
val paddedHexStrings = hexStrings.map(hex => if (hex.size == 1) "0"+hex else hex )
//logger.debug("Padded hex strings: " + paddedHexStrings)
//TODO: Figure out a better way to do this without calling .get on the result of fromByte
val constants = paddedHexStrings.map(ScriptConstantImpl(_))
constants
scriptConstants
}

View File

@ -17,7 +17,7 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter {
* @return
*/
def equal(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
require(stack.size > 1, "Stack size must be 2 or more to compare the top two values")
require(stack.size > 1, "Stack size must be 2 or more to compare the top two values for OP_EQUAL")
require(script.headOption.isDefined && script.head == OP_EQUAL, "Script operation must be OP_EQUAL")
logger.debug("Original stack: " + stack)

View File

@ -3,12 +3,15 @@ package org.scalacoin.script.constant
import org.scalacoin.util.ScalacoinUtil
import org.slf4j.LoggerFactory
import scala.annotation.tailrec
/**
* Created by chris on 1/24/16.
*/
trait ConstantInterpreter {
private def logger = LoggerFactory.getLogger(this.getClass())
/**
* The next byte contains the number of bytes to be pushed onto the stack.
* @param stack
@ -16,11 +19,8 @@ trait ConstantInterpreter {
*/
def opPushData1(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
require(script.headOption.isDefined && script.head == OP_PUSHDATA1, "Top of script stack must be OP_PUSHDATA1")
val numberOfBytesToBePushed : ScriptToken = script(1)
val numberOfBytes : Int = Integer.parseInt(numberOfBytesToBePushed.hex,16)
val numberOfBytes : Int = Integer.parseInt(script(1).hex,16)
val slicedScript = script.slice(2,script.size)
println("Slice script: " + slicedScript)
println("Number of bytes: " + numberOfBytes)
val (newStack,newScript) = opPushData(stack,slicedScript,numberOfBytes)
(newStack,newScript)
}
@ -33,11 +33,10 @@ trait ConstantInterpreter {
*/
def opPushData2(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
require(script.headOption.isDefined && script.head == OP_PUSHDATA2, "Top of script stack must be OP_PUSHDATA2")
val numberOfBytesToBePushed : String = script.slice(1,3).map(_.hex).mkString
logger.debug("Number of bytes to be pushed: " + numberOfBytesToBePushed)
val numberOfBytes : Int = Integer.parseInt(numberOfBytesToBePushed,16)
logger.debug("Parsed hex: " + numberOfBytes)
val slicedScript = script.slice(3,script.size)
//convert the hex string from little endian to big endian
val reversedHex = ScalacoinUtil.littleEndianToBigEndian(script(1).hex)
val numberOfBytes : Int = Integer.parseInt(reversedHex,16)
val slicedScript = script.slice(2,script.size)
val (newStack,newScript) = opPushData(stack,slicedScript,numberOfBytes)
(newStack,newScript)
}
@ -50,9 +49,10 @@ trait ConstantInterpreter {
*/
def opPushData4(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
require(script.headOption.isDefined && script.head == OP_PUSHDATA4, "Top of script stack must be OP_PUSHDATA4")
val numberOfBytesToBePushed : String = script.slice(1,5).map(_.hex).mkString
val numberOfBytes : Int = Integer.parseInt(numberOfBytesToBePushed,16)
val slicedScript = script.slice(5,script.size)
//convert the hex string from little endian to big endian
val reversedHex = ScalacoinUtil.littleEndianToBigEndian(script(1).hex)
val numberOfBytes : Int = Integer.parseInt(reversedHex,16)
val slicedScript = script.slice(2,script.size)
val (newStack,newScript) = opPushData(stack,slicedScript,numberOfBytes)
(newStack,newScript)
}
@ -71,4 +71,5 @@ trait ConstantInterpreter {
val newScript = script.slice(numberOfBytes,script.size)
(newStack,newScript)
}
}

View File

@ -9,6 +9,7 @@ import org.scalacoin.util.ScalacoinUtil
trait ScriptToken {
def hex : String
def bytes = ScalacoinUtil.decodeHex(hex)
}
trait ScriptOperation extends ScriptToken {

View File

@ -56,6 +56,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
case (scriptNumber : ScriptNumber) :: t => loop(scriptNumber :: stack, t)
case OP_PUSHDATA1 :: t => loop(opPushData1(stack,script))
case OP_PUSHDATA2 :: t => loop(opPushData2(stack,script))
case OP_PUSHDATA4 :: t => loop(opPushData4(stack,script))
//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

View File

@ -46,6 +46,11 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
parse(str) must equal (List(ScriptConstantImpl("417a"), OP_EQUAL))
}
it must "parse a script constant that has a leading zero" in {
val str = "0x0100"
parse(str) must equal (List(ScriptConstantImpl("0100")))
}
}

View File

@ -12,23 +12,24 @@ class ConstantInterpreterTest extends FlatSpec with MustMatchers with ConstantIn
val stack = List()
val script = List(OP_PUSHDATA1, ScriptNumberImpl(1), ScriptNumberImpl(7), OP_7, OP_EQUAL)
val (newStack,newScript) = opPushData1(stack,script)
newStack must be (List(ScriptNumberImpl(7)))
newScript must be (List(OP_7,OP_EQUAL))
}
it must "interpret OP_PUSHDATA2 correctly" in {
val stack = List()
val script = List(OP_PUSHDATA2, ScriptConstantImpl("100"), ScriptNumberImpl(8), OP_8, OP_EQUAL)
val script = List(OP_PUSHDATA2, ScriptConstantImpl("0100"), ScriptNumberImpl(8), OP_8, OP_EQUAL)
val (newStack,newScript) = opPushData2(stack,script)
newStack must be (List(OP_EQUAL, OP_8))
newScript must be (List())
newStack must be (List(ScriptNumberImpl(8)))
newScript must be (List(OP_8,OP_EQUAL))
}
it must "interpret OP_PUSHDATA4 correctly" in {
val stack = List()
val script = List(OP_PUSHDATA4, ScriptConstantImpl("1000000"), ScriptNumberImpl(9), OP_9, OP_EQUAL)
val script = List(OP_PUSHDATA4, ScriptConstantImpl("01000000"), ScriptNumberImpl(9), OP_9, OP_EQUAL)
val (newStack,newScript) = opPushData4(stack,script)
newStack must be (List(ScriptNumberImpl(9), ScriptConstantImpl("1000000")))
newStack must be (List(ScriptNumberImpl(9)))
newScript must be (List(OP_9, OP_EQUAL))
}
}