Skip to content

Commit

Permalink
add hex hashes for SHA256 and RIPEMD160
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed May 13, 2018
1 parent 1d2e943 commit 1a8e9e4
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ object BlockHeader {
timestamp: Long,
difficulty: Int,
nonce: Int): String =
SHA256.hashAll(
SHA256.hashStrings(
index.toString,
previousHash,
data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ 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
def hashViaMerklePath(init: String, path: Seq[String], index: Int): String =
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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ 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)))

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))

Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/com/github/fluency03/blockchain/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 1a8e9e4

Please sign in to comment.