Adding more documentation to types in the Script language class hierarchy

This commit is contained in:
Chris Stewart 2016-04-14 15:04:32 -05:00
parent 5b06e2e066
commit 9bb5f60bf4
4 changed files with 88 additions and 6 deletions

View file

@ -335,6 +335,10 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
logger.error("Cannot perform arithmetic operation on a number larger than 4 bytes, here is the number: " + s)
ScriptProgramFactory.factory(program,false)
}
case s : ScriptConstant =>
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(s.hex))
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
performUnaryArithmeticOperation(newProgram, op)
case s : ScriptToken =>
logger.error("Stack top must be a script number to perform an arithmetic operation")
ScriptProgramFactory.factory(program,false)
@ -364,9 +368,16 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptNumber, y : ScriptConstant) =>
//interpret y as a number
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptConstant, y : ScriptConstant) =>
//interpret x and y as a number
val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack)
performBinaryArithmeticOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) =>
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
ScriptProgramFactory.factory(program,false)
@ -390,7 +401,22 @@ trait ArithmeticInterpreter extends ControlOperationsInterpreter {
logger.error("Cannot perform boolean operation on a number larger than 4 bytes, one of these two numbers is larger than 4 bytes: " + x + " " + y)
ScriptProgramFactory.factory(program,false)
}
case (x : ScriptConstant, y : ScriptNumber) =>
//interpret x as a number
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
val newProgram = ScriptProgramFactory.factory(program, interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
performBinaryBooleanOperation(newProgram, op)
case (x : ScriptNumber, y : ScriptConstant) =>
//interpret y as a number
val interpretedNumber = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
val newProgram = ScriptProgramFactory.factory(program, x :: interpretedNumber :: program.stack.tail, ScriptProgramFactory.Stack)
performBinaryBooleanOperation(newProgram, op)
case (x : ScriptConstant, y : ScriptConstant) =>
//interpret x and y as a number
val interpretedNumberX = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(x.hex))
val interpretedNumberY = ScriptNumberFactory.fromNumber(BitcoinSUtil.hexToLong(y.hex))
val newProgram = ScriptProgramFactory.factory(program, interpretedNumberX :: interpretedNumberY :: program.stack.tail.tail, ScriptProgramFactory.Stack)
performBinaryBooleanOperation(newProgram, op)
case (x : ScriptToken, y : ScriptToken) =>
logger.error("The top two stack items must be script numbers to perform an arithmetic operation")
ScriptProgramFactory.factory(program,false)

View file

@ -6,21 +6,53 @@ import org.scalacoin.util.{BitcoinSUtil}
* Created by chris on 1/6/16.
*/
/**
* This is the root class of Script. Every element in the Script language is a
* ScriptToken - think of this the same way you think about Object in Java.
*/
sealed trait ScriptToken {
/**
* The hexadecimal representation of this script token
* @return
*/
def hex : String
/**
* The byte representation of this script token
* @return
*/
def bytes = BitcoinSUtil.decodeHex(hex)
/**
* The conversion from the byte representation of a token to a number
* @return
*/
def toLong = BitcoinSUtil.hexToLong(hex)
}
/**
* A script operation is an instruction that takes an input and gives an output
* Think of these as functions
*/
trait ScriptOperation extends ScriptToken {
def opCode : Int
override def hex : String = BitcoinSUtil.encodeHex(opCode.toByte)
}
/**
* A constant in the Script language for instance as String or a number
*/
sealed trait ScriptConstant extends ScriptToken
/**
* Represents a number in the Script language
*/
sealed trait ScriptNumber extends ScriptConstant {
/**
* The underlying number of the ScriptNumber
* @return
*/
def num : Long
def + (that : ScriptNumber) : ScriptNumber = ScriptNumberFactory.fromNumber(num + that.num)
@ -34,6 +66,13 @@ sealed trait ScriptNumber extends ScriptConstant {
def > (that : ScriptNumber) : Boolean = num > that.num
def >= (that : ScriptNumber) : Boolean = num >= that.num
/**
* This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
* are bitwise equivalent in Script. For instance ScriptNumber(0x01).numEqual(ScriptNumber(0x00000000001)) == true
* but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false
* @param that
* @return
*/
def numEqual(that : ScriptNumber) : Boolean = num == that.num
}
@ -46,11 +85,16 @@ sealed trait ScriptNumber extends ScriptConstant {
case class ScriptNumberImpl(num : Long, override val hex : String) extends ScriptNumber
/**
* Companion object for ScriptNumberImpl that gives us access to more constructor types for the
* ScriptNumberImpl case class
*/
object ScriptNumberImpl {
def apply(num : Long) : ScriptNumber = ScriptNumberImpl(num, BitcoinSUtil.longToHex(num))
def apply(hex : String) : ScriptNumber = ScriptNumberImpl(BitcoinSUtil.hexToLong(hex), hex)
def apply(bytes : Seq[Byte]) : ScriptNumber = ScriptNumberImpl(BitcoinSUtil.encodeHex(bytes))
}
sealed trait ScriptBoolean extends ScriptNumber
//TODO: Need to remove ScriptTrue & ScriptFalse - make OP_TRUE/FALSE inherit from ScriptBoolean

View file

@ -390,4 +390,16 @@ class ArithmeticInterpreterTest extends FlatSpec with MustMatchers with Arithmet
}
it must "interpret two script constants as numbers and then add them" in {
val scriptConstant1 = ScriptConstantFactory.fromHex("ffffffff")
val scriptConstant2 = ScriptConstantFactory.fromHex("ffffff7f")
val stack = List(scriptConstant1, scriptConstant2)
val script = List(OP_ADD)
val program = ScriptProgramFactory.factory(TestUtil.testProgram, stack, script)
val newProgram = opAdd(program)
newProgram.stack must be (List(OP_0))
newProgram.script.isEmpty must be (true)
}
}

View file

@ -29,12 +29,12 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
val source = scala.io.Source.fromFile("src/test/scala/org/scalacoin/script/interpreter/script_valid.json")
//use this to represent a single test case from script_valid.json
/* val lines =
val lines =
"""
|
|[["0x4c 0x00","0 EQUAL", "P2SH,STRICTENC"]]
""".stripMargin*/
val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close()
|[["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL", "P2SH,STRICTENC"]]
""".stripMargin
//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