diff --git a/src/main/scala/org/bitcoins/marshallers/transaction/RawTransactionOutputParser.scala b/src/main/scala/org/bitcoins/marshallers/transaction/RawTransactionOutputParser.scala index bf8a03db53..c1dc445d94 100644 --- a/src/main/scala/org/bitcoins/marshallers/transaction/RawTransactionOutputParser.scala +++ b/src/main/scala/org/bitcoins/marshallers/transaction/RawTransactionOutputParser.scala @@ -25,7 +25,7 @@ trait RawTransactionOutputParser extends RawBitcoinSerializer[Seq[TransactionOut //then call that function multiple times to get a Seq[TransactionOutput] val satoshisHex = BitcoinSUtil.encodeHex(bytes.take(8).reverse) logger.debug("Satoshi hex: " + satoshisHex) - val satoshis = Satoshis(java.lang.Long.parseLong(satoshisHex, 16)) + val satoshis = parseSatoshis(satoshisHex) //it doesn't include itself towards the size, thats why it is incremented by one val firstScriptPubKeyByte = 8 val scriptCompactSizeUIntSize : Int = BitcoinSUtil.parseCompactSizeUIntSize(bytes(firstScriptPubKeyByte)).toInt @@ -72,6 +72,18 @@ trait RawTransactionOutputParser extends RawBitcoinSerializer[Seq[TransactionOut if (compactSizeUIntHex == "00") satoshisHex + compactSizeUIntHex else satoshisHex + compactSizeUIntHex + output.scriptPubKey.hex } + + /** + * Parses the amount of satoshis a hex string represetns + * @param hex the hex string that is being parsed to satoshis + * @return the value of the hex string in satoshis + */ + private def parseSatoshis(hex : String) : Satoshis = { + if (hex == "ffffffffffffffff") { + //this represents a negative satoshis + CurrencyUnits.negativeSatoshi + } else Satoshis(java.lang.Long.parseLong(hex, 16)) + } } diff --git a/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala b/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala index 49f35b84a7..66cf3dd6f6 100644 --- a/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala +++ b/src/test/scala/org/bitcoins/protocol/transaction/TransactionTest.scala @@ -7,7 +7,7 @@ import org.bitcoins.script.ScriptProgram import org.bitcoins.script.flag.ScriptFlagFactory import org.bitcoins.protocol.transaction.testprotocol.CoreTransactionTestCaseProtocol._ import org.bitcoins.script.interpreter.ScriptInterpreter -import org.bitcoins.script.result.ScriptOk +import org.bitcoins.script.result.{ScriptError, ScriptOk} import org.bitcoins.util.{BitcoinSLogger, TestUtil, TransactionTestUtil} import org.scalatest.{FlatSpec, MustMatchers} @@ -56,7 +56,7 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger { |"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"] |] """.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[CoreTransactionTestCase]] = json.convertTo[Seq[Option[CoreTransactionTestCase]]] val testCases : Seq[CoreTransactionTestCase] = testCasesOpt.flatten @@ -79,6 +79,47 @@ class TransactionTest extends FlatSpec with MustMatchers with BitcoinSLogger { withClue(testCase.raw) { ScriptInterpreter.run(program) must equal (ScriptOk) } + }*/ + } + + it must "read all of the tx_invalid.json's contents and return a ScriptError" in { + + + val source = Source.fromURL(getClass.getResource("/tx_invalid.json")) + + + //use this to represent a single test case from script_valid.json +/* val lines = + """ + |[ + |[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], + |"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"] + | + |] + """.stripMargin*/ + val lines = try source.getLines.filterNot(_.isEmpty).map(_.trim) mkString "\n" finally source.close() + val json = lines.parseJson + val testCasesOpt : Seq[Option[CoreTransactionTestCase]] = json.convertTo[Seq[Option[CoreTransactionTestCase]]] + val testCases : Seq[CoreTransactionTestCase] = testCasesOpt.flatten + for { + testCase <- testCases + (outPoint,scriptPubKey) <- testCase.creditingTxsInfo + tx = testCase.spendingTx + (input,inputIndex) = findInput(tx,outPoint) + } yield { + logger.info("Raw test case: " + testCase.raw) + logger.info("Parsed ScriptSig: " + tx.inputs(inputIndex).scriptSignature) + logger.info("Sequence number: " + tx.inputs(inputIndex).sequence) + logger.info("ScriptPubKey: " + scriptPubKey) + logger.info("OutPoint: " + outPoint) + logger.info("Flags after parsing: " + testCase.flags) + require(outPoint.txId == input.previousOutput.txId, + "OutPoint txId not the same as input prevout txid\noutPoint.txId: " + outPoint.txId + "\n" + + "input prevout txid: " + input.previousOutput.txId) + val program = ScriptProgram(tx,scriptPubKey,inputIndex,testCase.flags) + withClue(testCase.raw) { + ScriptInterpreter.run(program).isInstanceOf[ScriptError] must equal (true) + } } }