From a71b62808fba9c2806d6802dcec39cc7be6883ee Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Thu, 31 Mar 2016 10:09:03 -0500 Subject: [PATCH] fixing various bugs, increasing test coverage --- .../org/scalacoin/protocol/Address.scala | 14 +++++--- .../protocol/script/ScriptPubKey.scala | 36 +++++++++---------- .../script/bitwise/BitwiseInterpreter.scala | 3 -- .../org/scalacoin/util/BitcoinSUtil.scala | 3 +- .../protocol/AddressFactoryTest.scala | 35 ++++++++++++++++++ .../protocol/BitcoinAddressTest.scala | 2 +- .../MultiSignatureScriptPubKeyTest.scala | 17 +++++++++ .../script/ScriptPubKeyFactoryTest.scala | 14 ++++++++ .../bitwise/BitwiseInterpreterTset.scala | 2 ++ .../bitwise/BitwiseOperationsTest.scala | 0 10 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 src/test/scala/org/scalacoin/protocol/AddressFactoryTest.scala rename src/{main => test}/scala/org/scalacoin/script/bitwise/BitwiseOperationsTest.scala (100%) diff --git a/src/main/scala/org/scalacoin/protocol/Address.scala b/src/main/scala/org/scalacoin/protocol/Address.scala index afcefb361d..850b65bc1b 100644 --- a/src/main/scala/org/scalacoin/protocol/Address.scala +++ b/src/main/scala/org/scalacoin/protocol/Address.scala @@ -3,6 +3,8 @@ package org.scalacoin.protocol import org.bitcoinj.core.{VersionedChecksummedBytes, Base58, Utils} import org.scalacoin.config.{RegTest, TestNet3, MainNet} +import scala.util.{Failure, Success, Try} + case class AddressInfo(bitcoinAddress: BitcoinAddress, n_tx: Long, total_received: Long, total_sent: Long, final_balance: Long) @@ -89,12 +91,16 @@ object BitcoinAddress { } object AssetAddress { - def validate(assetAddress : String) = { + def validate(assetAddress : String) : Boolean = { //asset addresses must have the one byte namespace equivalent to 19 //which ends up being 'a' in the ascii character set - val base58decodechecked : Array[Byte] = Base58.decodeChecked(assetAddress) - require(base58decodechecked != null) - base58decodechecked.size == 22 && base58decodechecked(0) == 0x13 + val base58DecodeChecked : Try[Array[Byte]] = Try(Base58.decodeChecked(assetAddress)) + base58DecodeChecked match { + case Success(bytes) => + if (bytes == null) false + else bytes.size == 22 && bytes(0) == 0x13 + case Failure(_) => false + } } /** diff --git a/src/main/scala/org/scalacoin/protocol/script/ScriptPubKey.scala b/src/main/scala/org/scalacoin/protocol/script/ScriptPubKey.scala index fa7e086506..d4a61caa89 100644 --- a/src/main/scala/org/scalacoin/protocol/script/ScriptPubKey.scala +++ b/src/main/scala/org/scalacoin/protocol/script/ScriptPubKey.scala @@ -23,22 +23,6 @@ sealed trait ScriptPubKey extends TransactionElement with BitcoinSLogger { */ def asm : Seq[ScriptToken] -/* /** - * Returns the script type of this scriptPubKey - * @return - */ - def scriptType : ScriptType = { - asm match { - case List(OP_DUP, OP_HASH160, BytesToPushOntoStackImpl(x), ScriptConstantImpl(pubKeyHash), OP_EQUALVERIFY, OP_CHECKSIG) => P2PKH - case List(OP_HASH160, BytesToPushOntoStackImpl(x), ScriptConstantImpl(scriptHash), OP_EQUAL) => P2SH - //TODO: make this more robust, this isn't the pattern that multsignature scriptPubKeys follow - case _ if (asm.last == OP_CHECKMULTISIG) => MultiSignature - case _ => NonStandard - } - }*/ - - //the addresses that the bitcoins correlated to the output - def addresses : Seq[BitcoinAddress] = ??? } @@ -61,15 +45,19 @@ trait MultiSignatureScriptPubKey extends ScriptPubKey { * Returns the amount of required signatures for this multisignature script pubkey output * @return */ - def requiredSigs = { + def requiredSigs : Long = { val asmWithoutPushOps = asm.filterNot(_.isInstanceOf[BytesToPushOntoStack]) val opCheckMultiSigIndex = if (asm.indexOf(OP_CHECKMULTISIG) != -1) asmWithoutPushOps.indexOf(OP_CHECKMULTISIG) else asmWithoutPushOps.indexOf(OP_CHECKMULTISIGVERIFY) + logger.debug("opCheckMultiSigIndex: " + opCheckMultiSigIndex) + logger.debug("maxSigs: " + maxSigs) + logger.debug("asmWithoutPushOps: " + asmWithoutPushOps) //magic number 2 represents the maxSig operation and the OP_CHECKMULTISIG operation at the end of the asm val numSigsRequired = asmWithoutPushOps(opCheckMultiSigIndex - maxSigs.toInt - 2) numSigsRequired match { case x : ScriptNumber => x.num case _ => throw new RuntimeException("The first element of the multisignature pubkey must be a script number operation\n" + - "scriptPubKey: " + this) + "operation: " + numSigsRequired + + "\nscriptPubKey: " + this) } } @@ -77,8 +65,7 @@ trait MultiSignatureScriptPubKey extends ScriptPubKey { * The maximum amount of signatures for this multisignature script pubkey output * @return */ - def maxSigs = { - val checkMultiSigIndex = asm.indexOf(OP_CHECKMULTISIG) + def maxSigs : Long = { if (checkMultiSigIndex == -1 || checkMultiSigIndex == 0) { //means that we do not have a max signature requirement 0.toLong @@ -90,6 +77,15 @@ trait MultiSignatureScriptPubKey extends ScriptPubKey { } } + + /** + * Gives the OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY index inside of asm + * @return the index of OP_CHECKMULTISIG or OP_CHECKMULTISIGVERIFY + */ + private def checkMultiSigIndex : Int = { + if (asm.indexOf(OP_CHECKMULTISIG) != -1) asm.indexOf(OP_CHECKMULTISIG) else asm.indexOf(OP_CHECKMULTISIGVERIFY) + } + /** * Returns the public keys encoded into the scriptPubKey * @return diff --git a/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala b/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala index f89dd826a7..2df4de5c04 100644 --- a/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala +++ b/src/main/scala/org/scalacoin/script/bitwise/BitwiseInterpreter.scala @@ -28,9 +28,6 @@ trait BitwiseInterpreter extends ControlOperationsInterpreter { val result = (h,h1) match { case (ScriptConstantImpl(x),ScriptConstantImpl(y)) => x == y - case (BytesToPushOntoStackImpl(x), BytesToPushOntoStackImpl(y)) => x == y - case (BytesToPushOntoStackImpl(x), ScriptConstantImpl(y)) => BytesToPushOntoStackImpl(x).hex == y - case (ScriptConstantImpl(x), BytesToPushOntoStackImpl(y)) => x == BytesToPushOntoStackImpl(y).hex case (ScriptConstantImpl(x), ScriptNumberImpl(y)) => BitcoinSUtil.hexToLong(x) == y case (ScriptNumberImpl(x), ScriptConstantImpl(y)) => x == BitcoinSUtil.hexToLong(y) case (OP_0, x) => OP_0.hex == x.hex diff --git a/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala b/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala index 0898d38aac..7e03eb209e 100644 --- a/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala +++ b/src/main/scala/org/scalacoin/util/BitcoinSUtil.scala @@ -65,7 +65,8 @@ trait BitcoinSUtil extends NumberUtil { def decodeBase58(base58 : String) : Seq[Byte] = Base58.decode(base58).toList - def encodeBase58(bytes : List[Byte]) : String = Base58.encode(bytes.toArray) + def encodeBase58(bytes : Seq[Byte]) : String = Base58.encode(bytes.toArray) + /** * Converts a little endian encoded hex string to a big endian encoded hex string diff --git a/src/test/scala/org/scalacoin/protocol/AddressFactoryTest.scala b/src/test/scala/org/scalacoin/protocol/AddressFactoryTest.scala new file mode 100644 index 0000000000..94bc804b97 --- /dev/null +++ b/src/test/scala/org/scalacoin/protocol/AddressFactoryTest.scala @@ -0,0 +1,35 @@ +package org.scalacoin.protocol + +import org.scalacoin.util.{BitcoinSUtil, TestUtil} +import org.scalatest.{FlatSpec, MustMatchers} + +/** + * Created by chris on 3/30/16. + */ +class AddressFactoryTest extends FlatSpec with MustMatchers { + + "AddressFactory" must "create an address from a base58 encoded string" in { + AddressFactory.factory(TestUtil.bitcoinAddress.value) must be (TestUtil.bitcoinAddress) + } + + it must "create an address from a sequence of bytes" in { + AddressFactory.fromBytes(BitcoinSUtil.decodeBase58(TestUtil.bitcoinAddress.value)) must be (TestUtil.bitcoinAddress) + } + + + it must "create an asset address from a base58 encoded string" in { + AddressFactory.fromBytes(BitcoinSUtil.decodeBase58(TestUtil.assetAddress.value)) must be (TestUtil.assetAddress) + } + + it must "throw an exception if the given string" in { + intercept[RuntimeException] { + AddressFactory.factory("01234567890abcdef") + } + } + + it must "throw an exception if we give a hex string to create a bitcoin address from" in { + intercept[RuntimeException] { + AddressFactory.fromHex("01234567890abcdef") + } + } +} diff --git a/src/test/scala/org/scalacoin/protocol/BitcoinAddressTest.scala b/src/test/scala/org/scalacoin/protocol/BitcoinAddressTest.scala index 9b1b2f1ec3..60b46262ce 100644 --- a/src/test/scala/org/scalacoin/protocol/BitcoinAddressTest.scala +++ b/src/test/scala/org/scalacoin/protocol/BitcoinAddressTest.scala @@ -48,7 +48,7 @@ class BitcoinAddressTest extends FlatSpec with MustMatchers { "An asset address with the first character replaced" must "not be a valid asset address" in { //3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy - intercept[org.bitcoinj.core.AddressFormatException] { + intercept[IllegalArgumentException] { val assetAddress = AssetAddress("aJ98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy") } } diff --git a/src/test/scala/org/scalacoin/protocol/script/MultiSignatureScriptPubKeyTest.scala b/src/test/scala/org/scalacoin/protocol/script/MultiSignatureScriptPubKeyTest.scala index 5c8c9d203b..f17b2489fd 100644 --- a/src/test/scala/org/scalacoin/protocol/script/MultiSignatureScriptPubKeyTest.scala +++ b/src/test/scala/org/scalacoin/protocol/script/MultiSignatureScriptPubKeyTest.scala @@ -60,4 +60,21 @@ class MultiSignatureScriptPubKeyTest extends FlatSpec with MustMatchers { } + it must "find the required signatures from a multisignature scriptPubKey using an OP_CHECKMULTISIGVERFIY" in { + val multiSigRawScriptPubKeyHex = "5221025878e270211662a27181cf" + + "4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d" + + "8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd0508" + + "69166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53af" + val scriptPubKey = ScriptPubKeyFactory.fromHex(multiSigRawScriptPubKeyHex) + val multiSigScriptPubKey : MultiSignatureScriptPubKey = scriptPubKey match { + case s : MultiSignatureScriptPubKey => s + case _ => throw new RuntimeException("Should be a multisig script pub key") + } + + multiSigScriptPubKey.maxSigs must be (3) + multiSigScriptPubKey.requiredSigs must be (2) + + } + + } diff --git a/src/test/scala/org/scalacoin/protocol/script/ScriptPubKeyFactoryTest.scala b/src/test/scala/org/scalacoin/protocol/script/ScriptPubKeyFactoryTest.scala index 86929c6d72..d165bd1f1a 100644 --- a/src/test/scala/org/scalacoin/protocol/script/ScriptPubKeyFactoryTest.scala +++ b/src/test/scala/org/scalacoin/protocol/script/ScriptPubKeyFactoryTest.scala @@ -45,5 +45,19 @@ class ScriptPubKeyFactoryTest extends FlatSpec with MustMatchers { } + it must "create a multisignature scriptPubKey from a script using OP_CHECKMULTISIGVERIFY" in { + val multiSigRawScriptPubKeyHex = "5221025878e270211662a27181cf" + + "4d6ad4d2cf0e69a98a3815c086f587c7e9388d87182103fc85980e3fac1f3d" + + "8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906210215b5bd0508" + + "69166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f53af" + val scriptPubKey = ScriptPubKeyFactory.fromHex(multiSigRawScriptPubKeyHex) + val isMultiSigScriptPubKey : Boolean = scriptPubKey match { + case s : MultiSignatureScriptPubKey => true + case _ => false + } + isMultiSigScriptPubKey must be (true) + + } + } diff --git a/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala b/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala index b7976127e0..68ee805991 100644 --- a/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala +++ b/src/test/scala/org/scalacoin/script/bitwise/BitwiseInterpreterTset.scala @@ -78,6 +78,8 @@ class BitwiseInterpreterTest extends FlatSpec with MustMatchers with BitwiseInte opEqual(program).stack.head must be (ScriptTrue) } + it must "evaluate two " + diff --git a/src/main/scala/org/scalacoin/script/bitwise/BitwiseOperationsTest.scala b/src/test/scala/org/scalacoin/script/bitwise/BitwiseOperationsTest.scala similarity index 100% rename from src/main/scala/org/scalacoin/script/bitwise/BitwiseOperationsTest.scala rename to src/test/scala/org/scalacoin/script/bitwise/BitwiseOperationsTest.scala