mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 02:39:18 +01:00
Fixing bug wrt to script numbers not pushing the correct amount of bytes onto the stack
This commit is contained in:
parent
787695dcdf
commit
7c631a52b7
7 changed files with 105 additions and 17 deletions
|
@ -70,11 +70,41 @@ trait ConstantInterpreter {
|
|||
val (newStack,newScript) = opPushData(stack,slicedScript,numberOfBytes)
|
||||
(newStack,newScript)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pushes the number of bytes onto the stack that is specified by script number on the script stack
|
||||
* @param stack
|
||||
* @param script
|
||||
* @return
|
||||
*/
|
||||
def pushScriptNumberBytesToStack(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
|
||||
require(script.headOption.isDefined && script.head.isInstanceOf[ScriptNumber], "Top of script must be a script number")
|
||||
require(script.size > 1, "Script size must be atleast to to push constants onto the stack")
|
||||
val bytesNeeded = script.head match {
|
||||
case scriptNumber : ScriptNumber => scriptNumber.opCode
|
||||
}
|
||||
/**
|
||||
* Parses the script tokens that need to be pushed onto our stack
|
||||
* @param scriptToken
|
||||
* @param accum
|
||||
* @return
|
||||
*/
|
||||
@tailrec
|
||||
def takeUntilBytesNeeded(scriptTokens : List[ScriptToken], accum : List[ScriptToken]) : (List[ScriptToken],List[ScriptToken]) = {
|
||||
if (accum.map(_.bytes.size).sum == bytesNeeded) (scriptTokens,accum)
|
||||
else if (accum.map(_.bytes.size).sum > bytesNeeded) throw new RuntimeException("We cannot have more bytes than what our script number specified")
|
||||
else takeUntilBytesNeeded(scriptTokens.tail, scriptTokens.head :: accum)
|
||||
}
|
||||
|
||||
val (newScript,tokensToBePushed) = takeUntilBytesNeeded(script.tail,List())
|
||||
|
||||
|
||||
(tokensToBePushed ++ stack, newScript)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for pushing the amount of bytes specified by the param numberOfBytes onto the stack
|
||||
* @param stack
|
||||
|
|
|
@ -34,9 +34,9 @@ trait ControlOperationsInterpreter {
|
|||
val (opElseIndex,opEndIfIndex) = findIndexesOpElseOpEndIf(script)
|
||||
stack.head match {
|
||||
case OP_0 =>
|
||||
//need to remove the statements from the script since
|
||||
//they should not be executed
|
||||
|
||||
//means that we need to execute the OP_ELSE statement if one exists
|
||||
//need to remove the OP_IF expression from the script
|
||||
//since it should not be executed
|
||||
require(opEndIfIndex.isDefined,"Every OP_IF must have a matching OP_ENDIF statement")
|
||||
//means that we have an else statement which needs to be executed
|
||||
if (opElseIndex.isDefined) {
|
||||
|
@ -50,6 +50,8 @@ trait ControlOperationsInterpreter {
|
|||
(stack.tail,newScript)
|
||||
}
|
||||
case _ =>
|
||||
//means that we need to execute the OP_IF expression
|
||||
//and delete its corresponding OP_ELSE if one exists
|
||||
if (opElseIndex.isDefined) {
|
||||
logger.debug("OP_ELSE index: " + opElseIndex.get)
|
||||
logger.debug("OP_ENDIF index: " + opEndIfIndex.get)
|
||||
|
@ -58,16 +60,15 @@ trait ControlOperationsInterpreter {
|
|||
val scriptPart1 = script.slice(1,opElseIndex.get)
|
||||
|
||||
val scriptWithoutOpElse = script.zipWithIndex.filter(_._2 != opElseIndex.get).map(_._1)
|
||||
//val scriptPart2 = script.slice(opEndIfIndex.get,script.size)
|
||||
//val newScript = scriptPart1 ++ scriptPart2
|
||||
|
||||
|
||||
val newOpElseIndex = findOpElse(scriptWithoutOpElse)
|
||||
|
||||
val scriptPart2 = if (newOpElseIndex.isDefined) {
|
||||
//means that we have another OP_ELSE before our OP_ENDIF.
|
||||
val scriptPart2 = if (newOpElseIndex.isDefined && newOpElseIndex.get < opEndIfIndex.get) {
|
||||
//the +1 is because we removed the OP_ELSE
|
||||
script.slice(newOpElseIndex.get+1,script.size)
|
||||
} else script.slice(opEndIfIndex.get,script.size)
|
||||
|
||||
val newScript = scriptPart1 ++ scriptPart2
|
||||
(stack.tail,newScript)
|
||||
} else (stack.tail,script.tail)
|
||||
|
|
|
@ -69,6 +69,21 @@ trait CryptoInterpreter extends ScalacoinUtil {
|
|||
???
|
||||
}
|
||||
|
||||
/**
|
||||
* The input is hashed using SHA-1.
|
||||
* @param stack
|
||||
* @param script
|
||||
* @return
|
||||
*/
|
||||
def opSha1(stack : List[ScriptToken], script : List[ScriptToken]) : (List[ScriptToken], List[ScriptToken]) = {
|
||||
require(script.headOption.isDefined && script.head == OP_SHA1, "Script top must be OP_SHA1")
|
||||
require(stack.headOption.isDefined, "We must have an element on the stack for OP_SHA1")
|
||||
|
||||
val constant = stack.head
|
||||
val hash = ScriptConstantImpl(CryptoUtil.sha1(constant.hex))
|
||||
(hash :: stack.tail, script.tail)
|
||||
|
||||
}
|
||||
|
||||
private def hashForSignature(inputScript : Seq[ScriptToken], spendingTx : Transaction,
|
||||
inputIndex : Int, hashType : HashType) : String = {
|
||||
|
@ -84,7 +99,6 @@ trait CryptoInterpreter extends ScalacoinUtil {
|
|||
|
||||
???
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ 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._
|
||||
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160, CryptoInterpreter}
|
||||
import org.scalacoin.script.crypto.{OP_SHA1, OP_CHECKSIG, OP_HASH160, CryptoInterpreter}
|
||||
import org.scalacoin.script.reserved.NOP
|
||||
import org.scalacoin.script.stack.{OP_DEPTH, StackInterpreter, OP_DUP}
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -57,10 +57,11 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
case ScriptConstantImpl(x) :: t if x == "1" => throw new RuntimeException("Not implemented yet")
|
||||
case ScriptConstantImpl(x) :: t if x == "0" => throw new RuntimeException("Not implemented yet")
|
||||
case (scriptNumberOp : ScriptNumberOperation) :: t => loop(scriptNumberOp.scriptNumber :: stack, t)
|
||||
case (scriptNumber : ScriptNumber) :: t => loop(scriptNumber :: stack, t)
|
||||
case (scriptNumber : ScriptNumber) :: t => loop(pushScriptNumberBytesToStack(stack,script))
|
||||
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))
|
||||
|
||||
|
@ -73,6 +74,7 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
|
|||
//crypto operations
|
||||
case OP_HASH160 :: t => loop(hash160(stack,script))
|
||||
case OP_CHECKSIG :: t => checkSig(stack,script,fullScript)
|
||||
case OP_SHA1 :: t => loop(opSha1(stack,script))
|
||||
|
||||
//reserved operations
|
||||
case (nop : NOP) :: t => loop((stack,t))
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.scalacoin.util
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash
|
||||
import org.scalacoin.script.constant.{ScriptConstantImpl, ScriptConstant}
|
||||
|
||||
|
@ -40,6 +42,23 @@ trait CryptoUtil extends ScalacoinUtil {
|
|||
val hash : List[Byte] = Sha256Hash.hashTwice(bytes.toArray).toList
|
||||
encodeHex(hash.reverse)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs SHA1(bytes)
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
def sha1(bytes : List[Byte]) : String = {
|
||||
val hashBytes = MessageDigest.getInstance("SHA-1").digest(bytes.toArray)
|
||||
encodeHex(hashBytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs SHA1(str)
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
def sha1(str : String) : String = sha1(str.map(_.toByte).toList)
|
||||
}
|
||||
|
||||
object CryptoUtil extends CryptoUtil
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.scalacoin.script.interpreter
|
|||
import java.io.File
|
||||
|
||||
import org.scalacoin.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
|
||||
import org.scalacoin.script.constant.{OP_0, OP_PUSHDATA1, ScriptToken}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160}
|
||||
import org.scalacoin.script.interpreter.testprotocol.{CoreTestCaseProtocol, CoreTestCase}
|
||||
import org.scalacoin.script.stack.OP_DUP
|
||||
|
@ -30,12 +30,14 @@ class ScriptInterpreterTest extends FlatSpec with MustMatchers with ScriptInterp
|
|||
}
|
||||
|
||||
|
||||
it must "evaluate a script with OP_PUSHDATA operations" in {
|
||||
val stack = List()
|
||||
val script = List(OP_PUSHDATA1, OP_0, OP_0, OP_EQUAL)
|
||||
|
||||
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 all valid scripts from the bitcoin core script_valid.json" in {
|
||||
import CoreTestCaseProtocol._
|
||||
|
||||
|
|
20
src/test/scala/org/scalacoin/util/CryptoUtilTest.scala
Normal file
20
src/test/scala/org/scalacoin/util/CryptoUtilTest.scala
Normal file
|
@ -0,0 +1,20 @@
|
|||
package org.scalacoin.util
|
||||
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
* Created by chris on 1/26/16.
|
||||
*/
|
||||
class CryptoUtilTest extends FlatSpec with MustMatchers {
|
||||
|
||||
|
||||
"CryptoUtil" must "perform a SHA-1 hash" in {
|
||||
val hash = CryptoUtil.sha1("")
|
||||
hash must be ("da39a3ee5e6b4b0d3255bfef95601890afd80709")
|
||||
}
|
||||
|
||||
it must "perform the correct SHA-1 hash on a mneomnic seed" in {
|
||||
val str = "The quick brown fox jumps over the lazy dog"
|
||||
CryptoUtil.sha1(str) must be ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue