From ea6866c3ac6afaf67a8ecf80cea6c0c96b60e48f Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Wed, 31 Aug 2016 10:31:09 -0500 Subject: [PATCH 1/6] Refactoring Address type to have a networkParameters, hash and address value, removing AssetAddress - we can support these later if needed --- .../crypto/TransactionSignatureCreator.scala | 1 + .../org/bitcoins/core/protocol/Address.scala | 119 +++++++----------- .../core/protocol/script/ScriptPubKey.scala | 4 +- .../protocol/script/ScriptSignature.scala | 1 + .../org/bitcoins/core/util/BitcoinSUtil.scala | 8 +- .../core/protocol/AddressFactoryTest.scala | 5 - .../bitcoins/core/protocol/AddressTest.scala | 16 --- .../core/protocol/BitcoinAddressTest.scala | 13 +- .../org/bitcoins/core/util/Base58Test.scala | 8 -- .../org/bitcoins/core/util/TestUtil.scala | 3 +- 10 files changed, 55 insertions(+), 123 deletions(-) diff --git a/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala b/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala index 87c7908e72..05239cd6b5 100644 --- a/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala +++ b/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala @@ -16,6 +16,7 @@ trait TransactionSignatureCreator { */ def createSig(txSignatureComponent: TransactionSignatureComponent, privateKey: ECPrivateKey, hashType: HashType): ECDigitalSignature = { val hash = TransactionSignatureSerializer.hashForSignature(txSignatureComponent, hashType) + val signature = privateKey.sign(hash) //append 1 byte hash type onto the end val sig = ECDigitalSignature(signature.bytes ++ Seq(hashType.byte)) diff --git a/src/main/scala/org/bitcoins/core/protocol/Address.scala b/src/main/scala/org/bitcoins/core/protocol/Address.scala index de80007477..2f33b4b394 100644 --- a/src/main/scala/org/bitcoins/core/protocol/Address.scala +++ b/src/main/scala/org/bitcoins/core/protocol/Address.scala @@ -2,20 +2,29 @@ package org.bitcoins.core.protocol import org.bitcoins.core.config._ import org.bitcoins.core.config.{MainNet, RegTest, TestNet3} import org.bitcoins.core.crypto.{ECPublicKey, Sha256Hash160Digest} +import org.bitcoins.core.protocol.transaction.TransactionOutput import org.bitcoins.core.protocol.script.{P2SHScriptPubKey, ScriptPubKey} import org.bitcoins.core.util.{Base58, CryptoUtil, Factory} - import scala.util.{Failure, Success, Try} sealed abstract class Address { + + /** The network that this address is valid for */ + def networkParameters: NetworkParameters + + /** The base58 string representation of this address */ def value : String + + /** Every address is derived from a [[Sha256Hash160Digest]] in a [[TransactionOutput]] */ + def hash: Sha256Hash160Digest } sealed trait BitcoinAddress extends Address sealed trait P2PKHAddress extends BitcoinAddress object P2PKHAddress { - private case class P2PKHAddressImpl(override val value: String) extends P2PKHAddress { + private case class P2PKHAddressImpl(value: String, hash: Sha256Hash160Digest, + networkParameters: NetworkParameters) extends P2PKHAddress { require(isP2PKHAddress(value), "Bitcoin address was invalid " + value) } @@ -30,11 +39,13 @@ object P2PKHAddress { val versionByte: Byte = network.p2pkhNetworkByte val bytes = Seq(versionByte) ++ hash.bytes val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) - P2PKHAddressImpl(Base58.encode(bytes ++ checksum)) + P2PKHAddress(Base58.encode(bytes ++ checksum), hash, network) } - def apply(value : String): P2PKHAddress = P2PKHAddressImpl(value) + def apply(value : String, hash: Sha256Hash160Digest, networkParameters: NetworkParameters): P2PKHAddress = { + P2PKHAddressImpl(value,hash,networkParameters) + } def apply(hash: Sha256Hash160Digest, networkParameters: NetworkParameters): P2PKHAddress = encodePubKeyHashToAddress(hash,networkParameters) @@ -75,7 +86,8 @@ sealed trait P2SHAddress extends BitcoinAddress * [[P2SHAddress]] companion object */ object P2SHAddress { - private case class P2SHAddressImpl(override val value: String) extends P2SHAddress { + private case class P2SHAddressImpl(value: String, hash: Sha256Hash160Digest, + networkParameters: NetworkParameters) extends P2SHAddress { require(isP2SHAddress(value), "Bitcoin address was invalid " + value) } @@ -106,10 +118,19 @@ object P2SHAddress { val hash = p2shScriptPubKey.scriptHash val bytes = Seq(versionByte) ++ hash.bytes val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) - P2SHAddressImpl(Base58.encode(bytes ++ checksum)) + P2SHAddress(Base58.encode(bytes ++ checksum),hash,network) } - def apply(value: String): P2SHAddress = P2SHAddressImpl(value) + def apply(value: String, hash160Digest: Sha256Hash160Digest, networkParameters: NetworkParameters): P2SHAddress = { + P2SHAddressImpl(value, hash160Digest, networkParameters) + } + + def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2SHAddress = { + val versionByte = network.p2shNetworkByte + val bytes = Seq(versionByte) ++ hash.bytes + val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) + P2SHAddress(Base58.encode(bytes ++ checksum), hash, network) + } /** * Checks if a address is a valid p2sh address * @@ -138,10 +159,6 @@ object P2SHAddress { } - -sealed trait AssetAddress extends Address - - object BitcoinAddress { def validate(bitcoinAddress: String): Boolean = { val illegalChars = List('O', 'I', 'l', '0') @@ -150,72 +167,28 @@ object BitcoinAddress { bitcoinAddress.filter(c => illegalChars.contains(c)).size == 0 } - /** - * Converts a bitcoin address to an asset address - * - * @param address - * @return - */ - def convertToAssetAddress(address : BitcoinAddress) : AssetAddress = { - val underlying : String = address.value - val decodedBase58 : Seq[Byte] = Base58.decode(underlying) - require ( - decodedBase58.size == 25 - ) - val decodedWithNameSpaceByte = Seq(0x13.toByte) ++ decodedBase58 - val split = decodedWithNameSpaceByte.splitAt(decodedWithNameSpaceByte.length - 4) - val data = split._1 - val newCheckSum = CryptoUtil.doubleSHA256(data).bytes.slice(0,4) - val constructedAssetAddress = data ++ newCheckSum - val encodedAssetAddress = Base58.encode(constructedAssetAddress) - AssetAddress(encodedAssetAddress) - } def apply(value: String): BitcoinAddress = { - if (P2PKHAddress.isP2PKHAddress(value)) P2PKHAddress(value) - else if (P2SHAddress.isP2SHAddress(value)) P2SHAddress(value) - else throw new IllegalArgumentException("The address was not a p2pkh or p2sh address, got: " + value) - } - - -} - -object AssetAddress { - private case class AssetAddressImpl(value : String) extends AssetAddress { - require(AssetAddress.validate(value), "The provided asset was invalid: " + value) - } - - 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. - //bytes size becomes 22 - val decodeCheckAssetAddress : Try[Seq[Byte]] = Base58.decodeCheck(assetAddress) - decodeCheckAssetAddress match { - case Success(bytes) => bytes.size == 22 && bytes.head == 0x13 - case Failure(_) => false + val decodeChecked = Base58.decodeCheck(value) + decodeChecked match { + case Success(bytes) => + val network = matchNetwork(bytes.head) + if (P2PKHAddress.isP2PKHAddress(value)) { + P2PKHAddress(Sha256Hash160Digest(bytes.tail),network) + } + else if (P2SHAddress.isP2SHAddress(value)) { + P2SHAddress(Sha256Hash160Digest(bytes.tail), network) + } else throw new IllegalArgumentException("The address was not a p2pkh or p2sh address, got: " + value) + case Failure(exception) => + throw exception } } - /** - * Converts an asset address into a bitcoin address - * - * @param assetAddress - * @return - */ - def convertToBitcoinAddress(assetAddress : AssetAddress) : BitcoinAddress = { - val underlying : String = assetAddress.value - val decodedAsset = Base58.decode(underlying) - require { - decodedAsset.size == 26 - } - val data = decodedAsset.slice(0, decodedAsset.length - 4) - val dataDroppedNameSpace = data.drop(1) - val checkSum = CryptoUtil.doubleSHA256(dataDroppedNameSpace).bytes.slice(0,4) - val value = Base58.encode(dataDroppedNameSpace ++ checkSum) - BitcoinAddress(value) + private def matchNetwork(byte: Byte): NetworkParameters = byte match { + case _ if Seq(MainNet.p2pkhNetworkByte,MainNet.p2shNetworkByte).contains(byte) => MainNet + case _ if Seq(TestNet3.p2pkhNetworkByte, TestNet3.p2shNetworkByte).contains(byte) => TestNet3 + case _ if Seq(RegTest.p2pkhNetworkByte,RegTest.p2shNetworkByte).contains(byte) => RegTest } - - def apply(value : String): AssetAddress = AssetAddressImpl(value) } object Address extends Factory[Address] { @@ -229,12 +202,10 @@ object Address extends Factory[Address] { * @return */ def factory(str : String) : Address = { - if (AssetAddress.validate(str)) AssetAddress(str) - else if (BitcoinAddress.validate(str)) BitcoinAddress(str) + if (BitcoinAddress.validate(str)) BitcoinAddress(str) else throw new RuntimeException("The address that you passed in is invalid") } - def fromBytes(bytes : Seq[Byte]) : Address = factory(Base58.encode(bytes)) override def fromHex(hex : String) : Address = throw new RuntimeException("We cannot create a bitcoin address from hex - bitcoin addresses are base 58 encoded") diff --git a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala index 1e01f628ee..13fa78d446 100644 --- a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala +++ b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala @@ -33,7 +33,7 @@ sealed trait ScriptPubKey extends NetworkElement with BitcoinSLogger { * https://bitcoin.org/en/developer-guide#pay-to-public-key-hash-p2pkh * Format: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG */ -trait P2PKHScriptPubKey extends ScriptPubKey +sealed trait P2PKHScriptPubKey extends ScriptPubKey object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] { @@ -72,7 +72,7 @@ object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] { * https://bitcoin.org/en/developer-guide#multisig * Format: [B pubkey] [C pubkey...] OP_CHECKMULTISIG */ -trait MultiSignatureScriptPubKey extends ScriptPubKey { +sealed trait MultiSignatureScriptPubKey extends ScriptPubKey { /** * Returns the amount of required signatures for this multisignature script pubkey output diff --git a/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala b/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala index 41e2f37fb3..d257184956 100644 --- a/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala +++ b/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala @@ -255,6 +255,7 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog redeemScriptTry match { case Success(redeemScript) => logger.debug("Possible redeemScript: " + redeemScript) + //logger.debug("Equals EmptyScriptPubKey: " + (EmptyScriptPubKey == redeemScript)) redeemScript match { case x : P2PKHScriptPubKey => true case x : MultiSignatureScriptPubKey => true diff --git a/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala b/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala index a72ac59517..bc6ff163e0 100644 --- a/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala +++ b/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala @@ -90,10 +90,10 @@ trait BitcoinSUtil { paddedHex } - /** Converts a sequence of bytes to a sequence of bit vectors - assumes the sequence of bytes are big endian */ + /** Converts a sequence of bytes to a sequence of bit vectors */ def bytesToBitVectors(bytes: Seq[Byte]): Seq[Seq[Boolean]] = bytes.map(byteToBitVector) - /** Converts a byte to a bit vector representing that byte - the bit vector is big endian */ + /** Converts a byte to a bit vector representing that byte */ def byteToBitVector(byte: Byte): Seq[Boolean] = { (0 to 7).map(index => isBitSet(byte,7 - index)) } @@ -101,12 +101,12 @@ trait BitcoinSUtil { /** Checks if the bit at the given index is set */ def isBitSet(byte: Byte, index: Int): Boolean = ((byte >> index) & 1) == 1 - /** Converts a bit vector to a single byte -- assumes the bits are big endian */ + /** Converts a bit vector to a single byte -- the resulting byte is big endian */ def bitVectorToByte(bits: Seq[Boolean]): Byte = { require(bits.size <= 8, "Cannot convert a bit vector to a byte when the size of the bit vector is larger than 8, got: " + bits) val b = bits.reverse val result: Seq[Int] = b.zipWithIndex.map { case (b, index) => - if (b) NumberUtil.pow2(index).toInt else 0 + if (b) NumberUtil.pow2(index).toInt else 0 } result.sum.toByte } diff --git a/src/test/scala/org/bitcoins/core/protocol/AddressFactoryTest.scala b/src/test/scala/org/bitcoins/core/protocol/AddressFactoryTest.scala index fca8b4382b..eda06f6507 100644 --- a/src/test/scala/org/bitcoins/core/protocol/AddressFactoryTest.scala +++ b/src/test/scala/org/bitcoins/core/protocol/AddressFactoryTest.scala @@ -16,11 +16,6 @@ class AddressFactoryTest extends FlatSpec with MustMatchers { Address(Base58.decode(TestUtil.bitcoinAddress.value)) must be (TestUtil.bitcoinAddress) } - - it must "create an asset address from a base58 encoded string" in { - Address(Base58.decode(TestUtil.assetAddress.value)) must be (TestUtil.assetAddress) - } - it must "throw an exception if the given string" in { intercept[RuntimeException] { Address("01234567890abcdef") diff --git a/src/test/scala/org/bitcoins/core/protocol/AddressTest.scala b/src/test/scala/org/bitcoins/core/protocol/AddressTest.scala index 817af39134..fa93aaa3bc 100644 --- a/src/test/scala/org/bitcoins/core/protocol/AddressTest.scala +++ b/src/test/scala/org/bitcoins/core/protocol/AddressTest.scala @@ -7,21 +7,5 @@ import org.scalatest.{FlatSpec, MustMatchers} * Created by chris on 3/23/15. */ class AddressTest extends FlatSpec with MustMatchers with BitcoinSLogger { - val assetAddress = TestUtil.assetAddress - "Addresses" must "be able to convert back and forth between a Bitcoin Address & an asset address" in { - val convertedOnce = BitcoinAddress.convertToAssetAddress(TestUtil.bitcoinAddress) - val actual : BitcoinAddress = AssetAddress.convertToBitcoinAddress(convertedOnce) - actual must be (TestUtil.bitcoinAddress) - val bitcoinAddress = AssetAddress.convertToBitcoinAddress(assetAddress) - val actualAssetAddress = BitcoinAddress.convertToAssetAddress(bitcoinAddress) - actualAssetAddress must be (assetAddress) - } - it must "allow type encapsulation for addresses" in { - - val bitcoinAddress : Address = TestUtil.bitcoinAddress - val assetAddress : Address = TestUtil.assetAddress - assetAddress must be (TestUtil.assetAddress) - bitcoinAddress must be (TestUtil.bitcoinAddress) - } } diff --git a/src/test/scala/org/bitcoins/core/protocol/BitcoinAddressTest.scala b/src/test/scala/org/bitcoins/core/protocol/BitcoinAddressTest.scala index fd25f0a912..40879fec0d 100644 --- a/src/test/scala/org/bitcoins/core/protocol/BitcoinAddressTest.scala +++ b/src/test/scala/org/bitcoins/core/protocol/BitcoinAddressTest.scala @@ -45,17 +45,6 @@ class BitcoinAddressTest extends FlatSpec with MustMatchers { } } - "akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA" must "be a valid asset address" in { - val assetAddress = AssetAddress("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA") - assetAddress.value must be ("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA") - } - - "An asset address with the first character replaced" must "not be a valid asset address" in { - //3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy - intercept[IllegalArgumentException] { - val assetAddress = AssetAddress("aJ98t1WpEZ73CNmQviecrnyiWrnqRhWNLyy") - } - } it must "encode a pubKeyHash to an address" in { //from https://stackoverflow.com/questions/19233053/hashing-from-a-public-key-to-a-bitcoin-address-in-php @@ -69,6 +58,6 @@ class BitcoinAddressTest extends FlatSpec with MustMatchers { val hex = "5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae" val scriptPubKey = ScriptPubKey(hex) val addr = P2SHAddress.encodeScriptPubKeyToAddress(scriptPubKey,MainNet) - addr must be (P2SHAddress("3P14159f73E4gFr7JterCCQh9QjiTjiZrG")) + addr must be (BitcoinAddress("3P14159f73E4gFr7JterCCQh9QjiTjiZrG")) } } \ No newline at end of file diff --git a/src/test/scala/org/bitcoins/core/util/Base58Test.scala b/src/test/scala/org/bitcoins/core/util/Base58Test.scala index 1140c5d204..322b5b2ce3 100644 --- a/src/test/scala/org/bitcoins/core/util/Base58Test.scala +++ b/src/test/scala/org/bitcoins/core/util/Base58Test.scala @@ -76,14 +76,6 @@ class Base58Test extends FlatSpec with MustMatchers with BitcoinSLogger { Base58.encode(Base58.decode(address)) must be ("1C4kYhyLftmkn48YarSoLupxHfYFo8kp64") } - it must "decode asset address into bytes then encode back to asset address" in { - //akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA - val asset = TestUtil.assetAddress.value - val bitcoinj = org.bitcoinj.core.Base58.encode(org.bitcoinj.core.Base58.decode(asset)) - Base58.encode(Base58.decode(asset)) must be ("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA") - Base58.encode(Base58.decode(asset)) must be (bitcoinj) - } - it must "decode multisig address into bytes then encode back to multisig" in { val multi = TestUtil.multiSigAddress.value val bitcoinj = org.bitcoinj.core.Base58.encode(org.bitcoinj.core.Base58.decode(multi)) diff --git a/src/test/scala/org/bitcoins/core/util/TestUtil.scala b/src/test/scala/org/bitcoins/core/util/TestUtil.scala index afceadf7c9..56470a1444 100644 --- a/src/test/scala/org/bitcoins/core/util/TestUtil.scala +++ b/src/test/scala/org/bitcoins/core/util/TestUtil.scala @@ -6,7 +6,7 @@ import org.bitcoins.core.serializers.transaction.{RawTransactionInputParser, Raw import org.bitcoins.core.policy.Policy import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.transaction.{EmptyTransaction, Transaction} -import org.bitcoins.core.protocol.{AssetAddress, BitcoinAddress} +import org.bitcoins.core.protocol.{BitcoinAddress} import org.bitcoins.core.script.{ExecutionInProgressScriptProgram, PreExecutionScriptProgram, ExecutedScriptProgram, ScriptProgram} import org.bitcoins.core.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY} import org.bitcoins.core.script.constant._ @@ -22,7 +22,6 @@ object TestUtil { def testP2SHAddress = BitcoinAddress("2MzYbQdkSVp5wVyMRp6A5PHPuQNHpiaTbCj") def bitcoinAddress = BitcoinAddress("1C4kYhyLftmkn48YarSoLupxHfYFo8kp64") def multiSigAddress = BitcoinAddress("342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey") - def assetAddress = AssetAddress("akJsoCcyh34FGPotxfEoSXGwFPCNAkyCgTA") val p2pkhInputScript = "473044022016ffdbb7c57634903c5e018fcfc48d59f4e37dc4bc3bbc9ba4e6ee39150bca030220119c2241a931819bc1a75d3596e4029d803d1cd6de123bf8a1a1a2c3665e1fac012102af7dad03e682fcd0427b5c24140c220ac9d8abe286c15f8cf5bf77eed19c3652" def p2pkhScriptSig = ScriptSignature(p2pkhInputScript) From f5bfacad4aec74bfe1ee25bac2b790e7b344e6d1 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Wed, 31 Aug 2016 11:08:01 -0500 Subject: [PATCH 2/6] Adding functionality to derive the base58 representation of an address instead of have it hard coded --- .../org/bitcoins/core/protocol/Address.scala | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/main/scala/org/bitcoins/core/protocol/Address.scala b/src/main/scala/org/bitcoins/core/protocol/Address.scala index 2f33b4b394..56b8defcef 100644 --- a/src/main/scala/org/bitcoins/core/protocol/Address.scala +++ b/src/main/scala/org/bitcoins/core/protocol/Address.scala @@ -20,10 +20,20 @@ sealed abstract class Address { } sealed trait BitcoinAddress extends Address -sealed trait P2PKHAddress extends BitcoinAddress + +sealed trait P2PKHAddress extends BitcoinAddress { + /** The base58 string representation of this address */ + override def value : String = { + val versionByte = networkParameters.p2pkhNetworkByte + val bytes = Seq(versionByte) ++ hash.bytes + val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) + Base58.encode(bytes ++ checksum) + } + +} object P2PKHAddress { - private case class P2PKHAddressImpl(value: String, hash: Sha256Hash160Digest, + private case class P2PKHAddressImpl(hash: Sha256Hash160Digest, networkParameters: NetworkParameters) extends P2PKHAddress { require(isP2PKHAddress(value), "Bitcoin address was invalid " + value) } @@ -35,17 +45,7 @@ object P2PKHAddress { * @param network the network on which this address is being generated for * @return */ - def encodePubKeyHashToAddress(hash: Sha256Hash160Digest, network: NetworkParameters): P2PKHAddress = { - val versionByte: Byte = network.p2pkhNetworkByte - val bytes = Seq(versionByte) ++ hash.bytes - val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) - P2PKHAddress(Base58.encode(bytes ++ checksum), hash, network) - } - - - def apply(value : String, hash: Sha256Hash160Digest, networkParameters: NetworkParameters): P2PKHAddress = { - P2PKHAddressImpl(value,hash,networkParameters) - } + def encodePubKeyHashToAddress(hash: Sha256Hash160Digest, network: NetworkParameters): P2PKHAddress = P2PKHAddressImpl(hash,network) def apply(hash: Sha256Hash160Digest, networkParameters: NetworkParameters): P2PKHAddress = encodePubKeyHashToAddress(hash,networkParameters) @@ -53,6 +53,7 @@ object P2PKHAddress { val hash = CryptoUtil.sha256Hash160(pubKey.bytes) P2PKHAddress(hash,networkParameters) } + /** * Checks if an address is a valid p2pkh address * @@ -80,13 +81,21 @@ object P2PKHAddress { } -sealed trait P2SHAddress extends BitcoinAddress +sealed trait P2SHAddress extends BitcoinAddress { + /** The base58 string representation of this address */ + override def value : String = { + val versionByte = networkParameters.p2shNetworkByte + val bytes = Seq(versionByte) ++ hash.bytes + val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) + Base58.encode(bytes ++ checksum) + } +} /** * [[P2SHAddress]] companion object */ object P2SHAddress { - private case class P2SHAddressImpl(value: String, hash: Sha256Hash160Digest, + private case class P2SHAddressImpl(hash: Sha256Hash160Digest, networkParameters: NetworkParameters) extends P2SHAddress { require(isP2SHAddress(value), "Bitcoin address was invalid " + value) } @@ -113,27 +122,13 @@ object P2SHAddress { def apply(scriptPubKey: ScriptPubKey,network: NetworkParameters): P2SHAddress = encodeScriptPubKeyToAddress(scriptPubKey,network) - def apply(p2shScriptPubKey: P2SHScriptPubKey, network: NetworkParameters): P2SHAddress = { - val versionByte = network.p2shNetworkByte - val hash = p2shScriptPubKey.scriptHash - val bytes = Seq(versionByte) ++ hash.bytes - val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) - P2SHAddress(Base58.encode(bytes ++ checksum),hash,network) - } + def apply(p2shScriptPubKey: P2SHScriptPubKey, network: NetworkParameters): P2SHAddress = P2SHAddress(p2shScriptPubKey.scriptHash,network) - def apply(value: String, hash160Digest: Sha256Hash160Digest, networkParameters: NetworkParameters): P2SHAddress = { - P2SHAddressImpl(value, hash160Digest, networkParameters) - } - def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2SHAddress = { - val versionByte = network.p2shNetworkByte - val bytes = Seq(versionByte) ++ hash.bytes - val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4) - P2SHAddress(Base58.encode(bytes ++ checksum), hash, network) - } + def apply(hash: Sha256Hash160Digest, network: NetworkParameters): P2SHAddress = P2SHAddressImpl(hash, network) + /** * Checks if a address is a valid p2sh address - * * @param address * @return */ From 0c6ad67a6eb61f51774e004315f7485794c4d751 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Wed, 31 Aug 2016 13:53:56 -0500 Subject: [PATCH 3/6] Adding some documention, removing uneeded code --- .../core/crypto/TransactionSignatureCreator.scala | 1 - .../scala/org/bitcoins/core/protocol/Address.scala | 11 ++++++----- .../core/protocol/script/ScriptSignature.scala | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala b/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala index 05239cd6b5..87c7908e72 100644 --- a/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala +++ b/src/main/scala/org/bitcoins/core/crypto/TransactionSignatureCreator.scala @@ -16,7 +16,6 @@ trait TransactionSignatureCreator { */ def createSig(txSignatureComponent: TransactionSignatureComponent, privateKey: ECPrivateKey, hashType: HashType): ECDigitalSignature = { val hash = TransactionSignatureSerializer.hashForSignature(txSignatureComponent, hashType) - val signature = privateKey.sign(hash) //append 1 byte hash type onto the end val sig = ECDigitalSignature(signature.bytes ++ Seq(hashType.byte)) diff --git a/src/main/scala/org/bitcoins/core/protocol/Address.scala b/src/main/scala/org/bitcoins/core/protocol/Address.scala index 56b8defcef..f3701b9f0c 100644 --- a/src/main/scala/org/bitcoins/core/protocol/Address.scala +++ b/src/main/scala/org/bitcoins/core/protocol/Address.scala @@ -7,7 +7,7 @@ import org.bitcoins.core.protocol.script.{P2SHScriptPubKey, ScriptPubKey} import org.bitcoins.core.util.{Base58, CryptoUtil, Factory} import scala.util.{Failure, Success, Try} -sealed abstract class Address { +sealed trait Address { /** The network that this address is valid for */ def networkParameters: NetworkParameters @@ -155,14 +155,14 @@ object P2SHAddress { } object BitcoinAddress { + /** Checks if the given base58 bitcoin address is a valid address */ def validate(bitcoinAddress: String): Boolean = { - val illegalChars = List('O', 'I', 'l', '0') - bitcoinAddress.length >= 26 && bitcoinAddress.length <= 35 && - (P2PKHAddress.isP2PKHAddress(bitcoinAddress) || P2SHAddress.isP2SHAddress(bitcoinAddress)) && - bitcoinAddress.filter(c => illegalChars.contains(c)).size == 0 + val decodeChecked = Base58.decodeCheck(bitcoinAddress) + decodeChecked.isSuccess } + /** Creates a [[BitcoinAddress]] from the given base58 string value */ def apply(value: String): BitcoinAddress = { val decodeChecked = Base58.decodeCheck(value) decodeChecked match { @@ -179,6 +179,7 @@ object BitcoinAddress { } } + /** Helper function for helping matching an address to a network byte */ private def matchNetwork(byte: Byte): NetworkParameters = byte match { case _ if Seq(MainNet.p2pkhNetworkByte,MainNet.p2shNetworkByte).contains(byte) => MainNet case _ if Seq(TestNet3.p2pkhNetworkByte, TestNet3.p2shNetworkByte).contains(byte) => TestNet3 diff --git a/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala b/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala index d257184956..41e2f37fb3 100644 --- a/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala +++ b/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala @@ -255,7 +255,6 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog redeemScriptTry match { case Success(redeemScript) => logger.debug("Possible redeemScript: " + redeemScript) - //logger.debug("Equals EmptyScriptPubKey: " + (EmptyScriptPubKey == redeemScript)) redeemScript match { case x : P2PKHScriptPubKey => true case x : MultiSignatureScriptPubKey => true From a2caf6c3b4705a594aa09c13967335b5661bdabd Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 2 Sep 2016 11:09:49 -0500 Subject: [PATCH 4/6] Removing sealed keywords on ScriptPubKey's --- .../org/bitcoins/core/protocol/script/ScriptPubKey.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala index 13fa78d446..8c644d817e 100644 --- a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala +++ b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala @@ -15,7 +15,7 @@ import scala.util.{Failure, Success, Try} /** * Created by chris on 12/26/15. */ -sealed trait ScriptPubKey extends NetworkElement with BitcoinSLogger { +trait ScriptPubKey extends NetworkElement with BitcoinSLogger { /** * Representation of a scriptSignature in a parsed assembly format @@ -33,7 +33,7 @@ sealed trait ScriptPubKey extends NetworkElement with BitcoinSLogger { * https://bitcoin.org/en/developer-guide#pay-to-public-key-hash-p2pkh * Format: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG */ -sealed trait P2PKHScriptPubKey extends ScriptPubKey +trait P2PKHScriptPubKey extends ScriptPubKey object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] { From b4f918857de1acf44fbb583e7156b3b0b900333f Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 2 Sep 2016 11:19:18 -0500 Subject: [PATCH 5/6] Removing sealed keywords on ScriptPubKey's --- .../org/bitcoins/core/protocol/script/ScriptPubKey.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala index 8c644d817e..1e01f628ee 100644 --- a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala +++ b/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala @@ -15,7 +15,7 @@ import scala.util.{Failure, Success, Try} /** * Created by chris on 12/26/15. */ -trait ScriptPubKey extends NetworkElement with BitcoinSLogger { +sealed trait ScriptPubKey extends NetworkElement with BitcoinSLogger { /** * Representation of a scriptSignature in a parsed assembly format @@ -72,7 +72,7 @@ object P2PKHScriptPubKey extends Factory[P2PKHScriptPubKey] { * https://bitcoin.org/en/developer-guide#multisig * Format: [B pubkey] [C pubkey...] OP_CHECKMULTISIG */ -sealed trait MultiSignatureScriptPubKey extends ScriptPubKey { +trait MultiSignatureScriptPubKey extends ScriptPubKey { /** * Returns the amount of required signatures for this multisignature script pubkey output From 911e1c6051270d6f17fae2c6fc1afb3f391c5f86 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Fri, 2 Sep 2016 21:01:33 -0500 Subject: [PATCH 6/6] Modifying assembly.sbt settings to not include relevant files to jar file --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index cd1205391a..55a18a3742 100644 --- a/build.sbt +++ b/build.sbt @@ -1 +1 @@ -import AssemblyKeys._ //test in assembly := {} testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "3") coverageExcludedPackages := ".*gen" coverageMinimum := 90 coverageFailOnMinimum := true \ No newline at end of file +import AssemblyKeys._ //test in assembly := {} testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "3") coverageExcludedPackages := ".*gen" coverageMinimum := 90 coverageFailOnMinimum := true mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => { case "logback.xml" => MergeStrategy.discard case x => old(x) } } assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false) \ No newline at end of file