From 1a8e9e49d983812e5c643b850b9f8f72f1c07db5 Mon Sep 17 00:00:00 2001 From: fluency03 Date: Sun, 13 May 2018 16:04:01 +0200 Subject: [PATCH] add hex hashes for SHA256 and RIPEMD160 --- .../fluency03/blockchain/core/BlockHeader.scala | 2 +- .../github/fluency03/blockchain/core/Merkle.scala | 6 +++--- .../fluency03/blockchain/core/Transaction.scala | 4 ++-- .../github/fluency03/blockchain/crypto/Base58.scala | 2 +- .../fluency03/blockchain/crypto/RIPEMD160.scala | 10 +++++++++- .../github/fluency03/blockchain/crypto/SHA256.scala | 10 ++++++++-- .../com/github/fluency03/blockchain/package.scala | 4 ++-- .../fluency03/blockchain/core/MerkleTest.scala | 12 ++++++------ .../fluency03/blockchain/crypto/RIPEMD160Test.scala | 10 +++++----- .../fluency03/blockchain/crypto/SHA256Test.scala | 8 ++++---- 10 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/main/scala/com/github/fluency03/blockchain/core/BlockHeader.scala b/src/main/scala/com/github/fluency03/blockchain/core/BlockHeader.scala index d66946e..d3fb8c4 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/BlockHeader.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/BlockHeader.scala @@ -57,7 +57,7 @@ object BlockHeader { timestamp: Long, difficulty: Int, nonce: Int): String = - SHA256.hashAll( + SHA256.hashStrings( index.toString, previousHash, data, diff --git a/src/main/scala/com/github/fluency03/blockchain/core/Merkle.scala b/src/main/scala/com/github/fluency03/blockchain/core/Merkle.scala index 20ff3e1..230cb34 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/Merkle.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/Merkle.scala @@ -18,7 +18,7 @@ object Merkle { case 0 => ZERO64 case 1 => hashes.head case n if n % 2 != 0 => computeRootOfHashes(hashes :+ hashes.last) // append last element again - case _ => computeRootOfHashes(hashes.grouped(2).map { a => SHA256.hashAll(a(0), a(1)) } .toList) + case _ => computeRootOfHashes(hashes.grouped(2).map { a => SHA256.hashStrings(a(0), a(1)) } .toList) } @tailrec @@ -26,9 +26,9 @@ object Merkle { if (path.isEmpty) init.toLowerCase() else { val newHash = if (index % 2 == 0) - SHA256.hashAll(init, path.head) + SHA256.hashStrings(init, path.head) else - SHA256.hashAll(path.head, init) + SHA256.hashStrings(path.head, init) hashViaMerklePath(newHash, path.tail, index/2) } diff --git a/src/main/scala/com/github/fluency03/blockchain/core/Transaction.scala b/src/main/scala/com/github/fluency03/blockchain/core/Transaction.scala index f01d9a3..4575840 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/Transaction.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/Transaction.scala @@ -65,13 +65,13 @@ object Transaction { hashOfTransaction(cbTx) == cbTx.id // hash of transaction - def hashOfTransaction(tx: Transaction): String = SHA256.hashAll( + def hashOfTransaction(tx: Transaction): String = SHA256.hashStrings( tx.txIns.map(tx => tx.previousOut.id + tx.previousOut.index).mkString, tx.txOuts.map(tx => tx.address + tx.amount).mkString, tx.timestamp.toString) def hashOfTransaction(txIns: Seq[TxIn], txOuts: Seq[TxOut], timestamp: Long): String = - SHA256.hashAll( + SHA256.hashStrings( txIns.map(tx => tx.previousOut.id + tx.previousOut.index).mkString, txOuts.map(tx => tx.address + tx.amount).mkString, timestamp.toString) diff --git a/src/main/scala/com/github/fluency03/blockchain/crypto/Base58.scala b/src/main/scala/com/github/fluency03/blockchain/crypto/Base58.scala index 3bc4447..fe6c7a5 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/Base58.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/Base58.scala @@ -10,7 +10,7 @@ object Base58 { lazy val ALPHABET: Array[Char] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray private val ENCODED_ZERO = ALPHABET(0) - def encodeString(str: String): Base58 = encode(str.getBytes) + def encodeString(text: String): Base58 = encode(text.getBytes) def encodeHex(hex: HexString): Base58 = encode(hex.hex2Bytes) diff --git a/src/main/scala/com/github/fluency03/blockchain/crypto/RIPEMD160.scala b/src/main/scala/com/github/fluency03/blockchain/crypto/RIPEMD160.scala index 99771b7..6c700a1 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/RIPEMD160.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/RIPEMD160.scala @@ -5,7 +5,9 @@ import org.bouncycastle.crypto.digests.RIPEMD160Digest object RIPEMD160 { - def hash(str: String): HexString = hash(str.getBytes) + def hashString(text: String): HexString = hash(text.getBytes) + + def hashHex(hex: HexString): HexString = hash(hex.hex2Bytes) def hash(bytes: Bytes): HexString = hashDigest(bytes).map("%02x".format(_)).mkString @@ -17,6 +19,12 @@ object RIPEMD160 { out } + def hashStrings(strings: String*): HexString = + hash(strings.map(_.getBytes).foldLeft(Array.empty[Byte])(_ ++ _)) + + def hashHexs(hexs: HexString*): HexString = + hash(hexs.map(_.hex2Bytes).foldLeft(Array.empty[Byte])(_ ++ _)) + def hash160(bytes: Bytes): HexString = hash(SHA256.hashDigest(bytes)) def hash160Digest(bytes: Bytes): Bytes = hashDigest(SHA256.hashDigest(bytes)) diff --git a/src/main/scala/com/github/fluency03/blockchain/crypto/SHA256.scala b/src/main/scala/com/github/fluency03/blockchain/crypto/SHA256.scala index a31eae2..616bf96 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/SHA256.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/SHA256.scala @@ -9,7 +9,9 @@ object SHA256 { * Generate SHA256 Hash from a input String. * https://gist.github.com/navicore/6234040bbfce3aa58f866db314c07c15 */ - def hash(text: String) : HexString = hash(text.getBytes) + def hashString(text: String) : HexString = hash(text.getBytes) + + def hashHex(hex: HexString) : HexString = hash(hex.hex2Bytes) def hash(bytes: Bytes) : HexString = String.format("%064x", new java.math.BigInteger(1, hashDigest(bytes))) @@ -17,7 +19,11 @@ object SHA256 { def hashDigest(bytes: Bytes): Bytes = MessageDigest.getInstance("SHA-256").digest(bytes) - def hashAll(strings: String*): HexString = hash(strings mkString "") + def hashStrings(strings: String*): HexString = + hash(strings.map(_.getBytes).foldLeft(Array.empty[Byte])(_ ++ _)) + + def hashHexs(hexs: HexString*): HexString = + hash(hexs.map(_.hex2Bytes).foldLeft(Array.empty[Byte])(_ ++ _)) def hash256(bytes: Bytes): HexString = hash(hashDigest(bytes)) diff --git a/src/main/scala/com/github/fluency03/blockchain/package.scala b/src/main/scala/com/github/fluency03/blockchain/package.scala index 6ae1a72..2db1d0f 100644 --- a/src/main/scala/com/github/fluency03/blockchain/package.scala +++ b/src/main/scala/com/github/fluency03/blockchain/package.scala @@ -46,8 +46,8 @@ package object blockchain { def hex2Bytes: Bytes = Hex.decode(str) def hex2Binary: BinaryString = binaryOfHex(str) def toBase64: Base64 = base64Of(str.getBytes) - def sha256: HexString = SHA256.hash(str) - def ripemd160: HexString = RIPEMD160.hash(str) + def sha256: HexString = SHA256.hashString(str) + def ripemd160: HexString = RIPEMD160.hashString(str) } implicit class BytesImplicit(val bytes: Bytes) { diff --git a/src/test/scala/com/github/fluency03/blockchain/core/MerkleTest.scala b/src/test/scala/com/github/fluency03/blockchain/core/MerkleTest.scala index 7cff845..274d565 100644 --- a/src/test/scala/com/github/fluency03/blockchain/core/MerkleTest.scala +++ b/src/test/scala/com/github/fluency03/blockchain/core/MerkleTest.scala @@ -24,21 +24,21 @@ class MerkleTest extends FlatSpec with Matchers { it should "be able to compute valid root hash." in { val h1 = "41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb" val h2 = "000031bee3fa033f2d69ae7d0d9f565bf3a235452ccf8a5edffb78cfbcdd7137" - val h12 = SHA256.hashAll(h1, h2) + val h12 = SHA256.hashStrings(h1, h2) computeRootOfHashes(List(h1, h2)) shouldEqual h12 val h3 = "000031beekdjnvj2310i0i0c4i3jomo1m2km10ijodsjco1edffb78cfbcdd7137" - val h33 = SHA256.hashAll(h3, h3) - computeRootOfHashes(List(h1, h2, h3)) shouldEqual SHA256.hashAll(h12, h33) + val h33 = SHA256.hashStrings(h3, h3) + computeRootOfHashes(List(h1, h2, h3)) shouldEqual SHA256.hashStrings(h12, h33) val t1 = createCoinbaseTx(1, genesisMiner, genesisTimestamp) val t2 = createCoinbaseTx(2, genesisMiner, genesisTimestamp) - val th12 = SHA256.hashAll(t1.id, t2.id) + val th12 = SHA256.hashStrings(t1.id, t2.id) computeRoot(List(t1, t2)) shouldEqual th12 val t3 = createCoinbaseTx(3, genesisMiner, genesisTimestamp) - val th33 = SHA256.hashAll(t3.id, t3.id) - computeRoot(List(t1, t2, t3)) shouldEqual SHA256.hashAll(th12, th33) + val th33 = SHA256.hashStrings(t3.id, t3.id) + computeRoot(List(t1, t2, t3)) shouldEqual SHA256.hashStrings(th12, th33) } it should "be able to compute root hash via merkle path and verify it." in { diff --git a/src/test/scala/com/github/fluency03/blockchain/crypto/RIPEMD160Test.scala b/src/test/scala/com/github/fluency03/blockchain/crypto/RIPEMD160Test.scala index 8d9c05a..8a72c94 100644 --- a/src/test/scala/com/github/fluency03/blockchain/crypto/RIPEMD160Test.scala +++ b/src/test/scala/com/github/fluency03/blockchain/crypto/RIPEMD160Test.scala @@ -18,12 +18,12 @@ class RIPEMD160Test extends FlatSpec with Matchers { "29368d7d9bb05cd8afbdf7705a6540d98028236965553f91bf1c5b4f70073f55b55d").hex2Bytes) shouldEqual "88C2D2FA846282C870A76CADECBE45C4ACD72BB6".toLowerCase - RIPEMD160.hash("61956bf4e271df1cd88a9a7828a59c88eb7ea13c176c4d03355ac27129760673") shouldEqual + RIPEMD160.hashString("61956bf4e271df1cd88a9a7828a59c88eb7ea13c176c4d03355ac27129760673") shouldEqual "352b0b6bd7284755d5c685fb7793c9f4d672c5ff" - RIPEMD160.hash("abcd") shouldEqual "2e7e536fd487deaa943fda5522d917bdb9011b7a" - RIPEMD160.hash("205575f4f33a39ff47f569613a694c6321d6cdd7") shouldEqual "bd4e962413308b4a6689aa0e7cff5e419391c3db" - RIPEMD160.hash("bitcoin") shouldEqual "5891bf40b0b0e8e19f524bdc2e842d012264624b" - RIPEMD160.hash("blockchain") shouldEqual "5c403af45cae136a79eea3c7e9f79c3dd049776b" + RIPEMD160.hashString("abcd") shouldEqual "2e7e536fd487deaa943fda5522d917bdb9011b7a" + RIPEMD160.hashString("205575f4f33a39ff47f569613a694c6321d6cdd7") shouldEqual "bd4e962413308b4a6689aa0e7cff5e419391c3db" + RIPEMD160.hashString("bitcoin") shouldEqual "5891bf40b0b0e8e19f524bdc2e842d012264624b" + RIPEMD160.hashString("blockchain") shouldEqual "5c403af45cae136a79eea3c7e9f79c3dd049776b" RIPEMD160.hash("0261107E41495C0DDC3850CD205D32896133EDD2ADDEBE7D8E20E01EFD078710".hex2Bytes) shouldEqual "A6020CF86FECEDEEF8859C172BAA1A15A575BC6F".toLowerCase diff --git a/src/test/scala/com/github/fluency03/blockchain/crypto/SHA256Test.scala b/src/test/scala/com/github/fluency03/blockchain/crypto/SHA256Test.scala index 1c89b2b..e159c4f 100644 --- a/src/test/scala/com/github/fluency03/blockchain/crypto/SHA256Test.scala +++ b/src/test/scala/com/github/fluency03/blockchain/crypto/SHA256Test.scala @@ -6,10 +6,10 @@ import org.scalatest.{FlatSpec, Matchers} class SHA256Test extends FlatSpec with Matchers { "SHA256" should "encode String to SHA256 and decode it back to original." in { - SHA256.hash("open sesame") shouldEqual "41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb" - SHA256.hashAll("open", " ", "sesame") shouldEqual "41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb" - SHA256.hash("") shouldEqual "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - SHA256.hash("0000000000000000000000000000000000000000000000000000000000000") shouldEqual "a738b0b5c122d30af5b9da1c63c5d590a31aeafa7de1723ee9b5e3a11c9def35" + SHA256.hashString("open sesame") shouldEqual "41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb" + SHA256.hashStrings("open", " ", "sesame") shouldEqual "41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb" + SHA256.hashString("") shouldEqual "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + SHA256.hashString("0000000000000000000000000000000000000000000000000000000000000") shouldEqual "a738b0b5c122d30af5b9da1c63c5d590a31aeafa7de1723ee9b5e3a11c9def35" SHA256.hash(("04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a25411" + "29368d7d9bb05cd8afbdf7705a6540d98028236965553f91bf1c5b4f70073f55b55d").hex2Bytes) shouldEqual