mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 09:52:09 +01:00
Fixing bug where script constants were not being pushed onto the stack as one script constant. I.e. 0x05 was pushing 5 individual script tokens onto the stack instead of one script token with the 5 script tokens inside of it
This commit is contained in:
parent
a617615cad
commit
45a688fd19
@ -92,16 +92,18 @@ trait ConstantInterpreter {
|
||||
* @return
|
||||
*/
|
||||
@tailrec
|
||||
def takeUntilBytesNeeded(scriptTokens : List[ScriptToken], accum : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
|
||||
val bytesSum = accum.map(_.bytesSize).sum
|
||||
if (bytesSum == bytesNeeded) (scriptTokens,accum)
|
||||
def takeUntilBytesNeeded(scriptTokens : List[ScriptToken], scriptConstantAccum : ScriptConstant) : (List[ScriptToken],ScriptConstant) = {
|
||||
val bytesSum = scriptConstantAccum.bytesSize
|
||||
if (bytesSum == bytesNeeded) (scriptTokens,scriptConstantAccum)
|
||||
else if (bytesSum > bytesNeeded) throw new RuntimeException("We cannot have more bytes than what our script number specified")
|
||||
else takeUntilBytesNeeded(scriptTokens.tail, scriptTokens.head :: accum)
|
||||
else takeUntilBytesNeeded(scriptTokens.tail, ScriptConstantImpl(scriptConstantAccum.hex + scriptTokens.head.hex))
|
||||
}
|
||||
|
||||
val (newScript,tokensToBePushed) = takeUntilBytesNeeded(script.tail,List())
|
||||
(tokensToBePushed ++ stack, newScript)
|
||||
|
||||
val (newScript,newScriptConstant) = takeUntilBytesNeeded(script.tail,ScriptConstantImpl(""))
|
||||
//see if the new script constant can be converted into a script number
|
||||
val scriptNumber : Option[ScriptNumber] = ScriptNumberFactory.fromHex(newScriptConstant.hex)
|
||||
if (scriptNumber.isDefined) (scriptNumber.get :: stack, newScript)
|
||||
else (newScriptConstant :: stack, newScript)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,13 +94,6 @@ case object OP_FALSE extends ScriptNumberOperation {
|
||||
override def opCode = OP_0.opCode
|
||||
override def scriptNumber = OP_0.scriptNumber
|
||||
}
|
||||
/**
|
||||
* The number -1 is pushed onto the stack.
|
||||
*/
|
||||
case object OP_1NEGATE extends ScriptNumberOperation {
|
||||
override def opCode = 79
|
||||
override def scriptNumber = ScriptNumberFactory.factory(-1).get
|
||||
}
|
||||
|
||||
/**
|
||||
* The number 1 is pushed onto the stack.
|
||||
@ -110,6 +103,15 @@ case object OP_TRUE extends ScriptNumberOperation {
|
||||
override def scriptNumber = ScriptNumberFactory.factory(1).get
|
||||
}
|
||||
|
||||
/**
|
||||
* The number -1 is pushed onto the stack.
|
||||
*/
|
||||
case object OP_1NEGATE extends ScriptNumberOperation {
|
||||
override def opCode = 79
|
||||
override def scriptNumber = ScriptNumberFactory.factory(-1).get
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The number 1 is pushed onto the stack.
|
||||
*/
|
||||
|
@ -128,6 +128,20 @@ trait ControlOperationsInterpreter {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks transaction as invalid if top stack value is not true.
|
||||
* @param stack
|
||||
* @param script
|
||||
* @return
|
||||
*/
|
||||
def opVerify(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken],Boolean) = {
|
||||
require(script.headOption.isDefined && script.head == OP_VERIFY, "Script top must be OP_VERIFY")
|
||||
require(stack.size > 0, "Stack must have at least one element on it to run OP_VERIFY")
|
||||
if (stack.head != OP_0) {
|
||||
(stack,script.tail,true)
|
||||
} else (stack,script.tail,false)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a list of script tokens into its corresponding binary tree
|
||||
|
@ -71,6 +71,10 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
||||
case OP_ELSE :: t => loop(opElse(stack,script))
|
||||
case OP_ENDIF :: t => loop(opEndIf(stack,script))
|
||||
case OP_RETURN :: t => opReturn(stack,script)
|
||||
case OP_VERIFY :: t =>
|
||||
val (newStack,newScript,result) = opVerify(stack,script)
|
||||
if (result) loop(newStack,newScript)
|
||||
else false
|
||||
|
||||
//crypto operations
|
||||
case OP_HASH160 :: t => loop(hash160(stack,script))
|
||||
|
@ -2,7 +2,7 @@ package org.scalacoin.marshallers.script
|
||||
|
||||
import org.scalacoin.script.arithmetic.OP_ADD
|
||||
import org.scalacoin.script.bitwise.OP_EQUAL
|
||||
import org.scalacoin.script.constant.{OP_1NEGATE, ScriptConstantImpl}
|
||||
import org.scalacoin.script.constant.{OP_1, OP_1NEGATE, ScriptConstantImpl}
|
||||
import org.scalacoin.util.{ScalacoinUtil, TestUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
@ -60,4 +60,6 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.io.File
|
||||
|
||||
import org.scalacoin.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.control.OP_VERIFY
|
||||
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160}
|
||||
import org.scalacoin.script.interpreter.testprotocol.{CoreTestCaseProtocol, CoreTestCase}
|
||||
import org.scalacoin.script.stack.OP_DUP
|
||||
@ -33,10 +34,17 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|
||||
it must "evaluate a script that asks to push 20 bytes onto the stack correctly" in {
|
||||
val stack = List(ScriptConstantImpl("68ca4fec736264c13b859bac43d5173df6871682"))
|
||||
val script = List(ScriptNumberImpl(20), ScriptConstantImpl("68ca4fec736264c13b859bac43d5173df6871682"), OP_EQUAL)
|
||||
|
||||
run(stack,script) must be (true)
|
||||
}
|
||||
|
||||
it must "evaluate a 5 byte representation of 0x0000000001 as 0x01 when pushed onto the stack" in {
|
||||
//this is for the following test case inside of script_valid.json
|
||||
//["1 0x05 0x01 0x00 0x00 0x00 0x00", "VERIFY", "P2SH,STRICTENC", "values >4 bytes can be cast to boolean"]
|
||||
val stack = List(OP_1)
|
||||
val script = List(ScriptNumberImpl(5), ScriptNumberImpl(1), OP_0, OP_0, OP_0, OP_0,OP_VERIFY)
|
||||
run(stack,script) must equal (true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -46,13 +54,13 @@ 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 =
|
||||
"""
|
||||
|
|
||||
|[["1", "NOTIF 0 NOTIF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 0 NOTIF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "P2SH,STRICTENC"]]
|
||||
""".stripMargin
|
||||
|[["1 0x05 0x01 0x00 0x00 0x00 0x00", "VERIFY", "P2SH,STRICTENC", "values >4 bytes can be cast to boolean"]]
|
||||
""".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
|
||||
|
Loading…
Reference in New Issue
Block a user