From 019d5f3b99dbb71eaba09f12fa9dcaff0e26c20f Mon Sep 17 00:00:00 2001 From: fluency03 Date: Sat, 12 May 2018 15:08:35 +0200 Subject: [PATCH] add tome TODOs & hash functions --- .../github/fluency03/blockchain/Serde.scala | 26 +++++++++++++++++++ .../fluency03/blockchain/core/Block.scala | 1 + .../fluency03/blockchain/core/Merkle.scala | 17 ++++++++++++ .../blockchain/core/Transaction.scala | 1 + .../blockchain/crypto/RIPEMD160.scala | 5 ++-- .../fluency03/blockchain/crypto/SHA256.scala | 3 +++ .../blockchain/crypto/Secp256k1.scala | 3 +++ .../github/fluency03/blockchain/package.scala | 13 ++++++---- .../blockchain/crypto/RIPEMD160Test.scala | 4 +-- 9 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 src/main/scala/com/github/fluency03/blockchain/Serde.scala diff --git a/src/main/scala/com/github/fluency03/blockchain/Serde.scala b/src/main/scala/com/github/fluency03/blockchain/Serde.scala new file mode 100644 index 0000000..c31fc97 --- /dev/null +++ b/src/main/scala/com/github/fluency03/blockchain/Serde.scala @@ -0,0 +1,26 @@ +package com.github.fluency03.blockchain + +import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream} + +/** + * Ser(ialization) and De(serialization) + */ +object Serde { + + def serialize[T](value: T): Bytes = { + val stream: ByteArrayOutputStream = new ByteArrayOutputStream() + val oos = new ObjectOutputStream(stream) + oos.writeObject(value) + oos.close() + stream.close() + stream.toByteArray + } + + def deserialize[T](bytes: Array[Byte]): T = { + val ois = new ObjectInputStream(new ByteArrayInputStream(bytes)) + val value = ois.readObject + ois.close() + value.asInstanceOf[T] + } + +} diff --git a/src/main/scala/com/github/fluency03/blockchain/core/Block.scala b/src/main/scala/com/github/fluency03/blockchain/core/Block.scala index f2eab8f..8169a29 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/Block.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/Block.scala @@ -92,6 +92,7 @@ object Block { 0, ZERO64, "Welcome to Blockchain in Scala!", genesisTimestamp, difficulty, Seq(createCoinbaseTx(0, genesisMiner, genesisTimestamp))) + // TODO (Chang): implement parallel version def mineNextBlock( nextIndex: Int, prevHash: String, 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 187ace5..1aad887 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/Merkle.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/Merkle.scala @@ -5,9 +5,14 @@ import com.github.fluency03.blockchain.crypto.SHA256 import scala.annotation.tailrec +// TODO (Chang): implement a full nodes merkle tree + + object Merkle { def computeRoot(trans: Seq[Transaction]): String = computeRootOfHashes(trans.map(_.id)) + // TODO (Chang): double SHA256 hash instead of single. + // TODO (Chang): hash and concatenate hash string or hash bytes? @tailrec def computeRootOfHashes(hashes: Seq[String]): String = hashes.length match { case 0 => ZERO64 @@ -16,4 +21,16 @@ object Merkle { case _ => computeRootOfHashes(hashes.grouped(2).map { a => SHA256.hashAll(a(0), a(1)) } .toList) } + @tailrec + def hashViaMerklePath(init: String, path: Seq[String], index: Int): String = + if (path.isEmpty) init + else { + val newHash = if (index % 2 == 0) SHA256.hashAll(init, path.head) else SHA256.hashAll(path.head, init) + hashViaMerklePath(newHash, path.tail, index/2) + } + + def simplifiedPaymentVerification(init: String, root: String, path: Seq[String], index: Int): Boolean = + hashViaMerklePath(init, path, index) == root + + } 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 920430c..1dfddd7 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/Transaction.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/Transaction.scala @@ -12,6 +12,7 @@ import org.json4s.{Extraction, JValue} import scala.collection.mutable case class Outpoint(id: String, index: Int) +// TODO (Chang): add public key into TxIn case class TxIn(previousOut: Outpoint, signature: String) case class TxOut(address: String, amount: Long) 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 e419135..13af413 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/RIPEMD160.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/RIPEMD160.scala @@ -17,9 +17,8 @@ object RIPEMD160 { out } - def doubleHash(bytes: Bytes): String = hash(SHA256.hashToDigest(bytes)) - - def doubleHashToDigest(bytes: Bytes): Bytes = hashToDigest(SHA256.hashToDigest(bytes)) + def hash160(bytes: Bytes): String = hash(SHA256.hashToDigest(bytes)) + def hash160ToDigest(bytes: Bytes): Bytes = hashToDigest(SHA256.hashToDigest(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 e1c10a4..fd022eb 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/SHA256.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/SHA256.scala @@ -28,5 +28,8 @@ object SHA256 { */ def hashAll(strings: String*): String = hash(strings mkString "") + def hash256(bytes: Bytes): String = hash(hashToDigest(bytes)) + + def hash256ToDigest(bytes: Bytes): Bytes = hashToDigest(hashToDigest(bytes)) } diff --git a/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala b/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala index 72f6bb6..3fdc6f0 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala @@ -90,6 +90,9 @@ object Secp256k1 { def publicKeyToAddress(publicKey: PublicKey, networkBytes: String = "00"): String = Base58.checkEncode(networkBytes.hex2Bytes ++ publicKeyToBytes(publicKey).toHash160Digest) + def hash160ToAddress(hash160: String, networkBytes: String = "00"): String = + Base58.checkEncode(networkBytes.hex2Bytes ++ hash160.hex2Bytes) + def addressToHash160(address: String, networkBytes: String = "00"): (String, String) = { val decoded: Bytes = Base58.decode(address) val (preBytes, fourBytes) = decoded.splitAt(decoded.length - 4) diff --git a/src/main/scala/com/github/fluency03/blockchain/package.scala b/src/main/scala/com/github/fluency03/blockchain/package.scala index b8af251..7c03938 100644 --- a/src/main/scala/com/github/fluency03/blockchain/package.scala +++ b/src/main/scala/com/github/fluency03/blockchain/package.scala @@ -4,7 +4,7 @@ import java.nio.charset.Charset import java.security.{PrivateKey, PublicKey} import java.time.Instant -import com.github.fluency03.blockchain.crypto.Secp256k1.{privateKeyToHex, publicKeyToHex} +import com.github.fluency03.blockchain.crypto.Secp256k1 import com.github.fluency03.blockchain.core.{Peer, PeerSimple, TxIn, TxOut} import com.github.fluency03.blockchain.crypto.{RIPEMD160, SHA256, Secp256k1} import org.bouncycastle.util.encoders.{Base64, Hex} @@ -59,17 +59,20 @@ package object blockchain { def toSha256Digest: Bytes = SHA256.hashToDigest(bytes) def toRipemd160: String = RIPEMD160.hash(bytes) def toRipemd160ODigest: Bytes = RIPEMD160.hashToDigest(bytes) - def toHash160: String = RIPEMD160.doubleHash(bytes) - def toHash160Digest: Bytes = RIPEMD160.doubleHashToDigest(bytes) + def toHash160: String = RIPEMD160.hash160(bytes) + def toHash160Digest: Bytes = RIPEMD160.hash160ToDigest(bytes) } implicit class PublicKeyImplicit(val publicKey: PublicKey) { - def toHex: String = publicKeyToHex(publicKey) + def toHex: String = Secp256k1.publicKeyToHex(publicKey) + def toBytes: Bytes = Secp256k1.publicKeyToBytes(publicKey) + def toHash160: String = Secp256k1.publicKeyToBytes(publicKey).toHash160 def address: String = Secp256k1.publicKeyToAddress(publicKey) } implicit class PrivateKeyImplicit(val privateKey: PrivateKey) { - def toHex: String = privateKeyToHex(privateKey) + def toHex: String = Secp256k1.privateKeyToHex(privateKey) + def toBytes: Bytes = Secp256k1.privateKeyToBytes(privateKey) } implicit class PeerImplicit(val peer: Peer) { 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 dc5294f..0ad9efe 100644 --- a/src/test/scala/com/github/fluency03/blockchain/crypto/RIPEMD160Test.scala +++ b/src/test/scala/com/github/fluency03/blockchain/crypto/RIPEMD160Test.scala @@ -10,11 +10,11 @@ class RIPEMD160Test extends FlatSpec with Matchers { RIPEMD160.hash("173BDED8F2A2069C193E63EA30DC8FD20E815EC3642B9C24AD7002C03D1BFB9B".hex2Bytes) shouldEqual "88C2D2FA846282C870A76CADECBE45C4ACD72BB6".toLowerCase - RIPEMD160.doubleHashToDigest(("04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a25411" + + RIPEMD160.hash160ToDigest(("04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a25411" + "29368d7d9bb05cd8afbdf7705a6540d98028236965553f91bf1c5b4f70073f55b55d").hex2Bytes) shouldEqual "88C2D2FA846282C870A76CADECBE45C4ACD72BB6".hex2Bytes - RIPEMD160.doubleHash(("04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a25411" + + RIPEMD160.hash160(("04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a25411" + "29368d7d9bb05cd8afbdf7705a6540d98028236965553f91bf1c5b4f70073f55b55d").hex2Bytes) shouldEqual "88C2D2FA846282C870A76CADECBE45C4ACD72BB6".toLowerCase