From f3e27cac1e35c442b2eb240f998ab6e9ea0c9510 Mon Sep 17 00:00:00 2001 From: fluency03 Date: Thu, 26 Apr 2018 12:05:16 +0100 Subject: [PATCH 1/2] add GenericRoutesTest --- .../scala/com/fluency03/blockchain/Util.scala | 21 ++++++++ .../fluency03/blockchain/api/Message.scala | 4 +- .../fluency03/blockchain/api/package.scala | 3 ++ .../blockchain/api/routes/BlockRoutes.scala | 8 +-- .../api/routes/BlockchainRoutes.scala | 8 +-- .../blockchain/api/routes/GenericRoutes.scala | 28 +++++------ .../blockchain/api/routes/NetworkRoutes.scala | 8 +-- .../api/routes/TransactionRoutes.scala | 8 +-- .../blockchain/api/routes/package.scala | 16 ++++++ .../blockchain/api/MessageTest.scala | 4 +- .../api/routes/BlockRoutesTest.scala | 5 +- .../api/routes/BlockchainRoutesTest.scala | 9 +++- .../api/routes/GenericRoutesTest.scala | 49 ++++++++++++++++++- .../api/routes/NetworkRoutesTest.scala | 5 +- .../api/routes/TransactionRoutesTest.scala | 5 +- 15 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 src/main/scala/com/fluency03/blockchain/api/routes/package.scala diff --git a/src/main/scala/com/fluency03/blockchain/Util.scala b/src/main/scala/com/fluency03/blockchain/Util.scala index 8c02304..8eb9b2a 100644 --- a/src/main/scala/com/fluency03/blockchain/Util.scala +++ b/src/main/scala/com/fluency03/blockchain/Util.scala @@ -2,6 +2,9 @@ package com.fluency03.blockchain import java.security.MessageDigest import java.time.Instant + +import akka.http.scaladsl.model.{StatusCode, StatusCodes} +import com.fluency03.blockchain.api.{FailureMsg, Message, SuccessMsg} import org.bouncycastle.util.encoders.Base64 object Util { @@ -60,4 +63,22 @@ object Util { */ def fromBase64(base64: String): String = new String(Base64.decode(base64), "UTF-8") + /** + * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). + */ + def failsafeMsg(fun: => String): Message = + try { SuccessMsg(fun) } + catch { + case e: Exception => FailureMsg(e.getMessage) + } + + /** + * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). + */ + def failsafeResp(fun: => String): (StatusCode, Message) = + try { (StatusCodes.OK, SuccessMsg(fun)) } + catch { + case e: Exception => (StatusCodes.InternalServerError, FailureMsg(e.getMessage)) + } + } diff --git a/src/main/scala/com/fluency03/blockchain/api/Message.scala b/src/main/scala/com/fluency03/blockchain/api/Message.scala index 010bc45..009208a 100644 --- a/src/main/scala/com/fluency03/blockchain/api/Message.scala +++ b/src/main/scala/com/fluency03/blockchain/api/Message.scala @@ -3,5 +3,5 @@ package com.fluency03.blockchain.api final case class Input(content: String) sealed trait Message -final case class SuccessMsg(content: String) extends Message -final case class FailureMsg(content: String) extends Message +final case class SuccessMsg(message: String) extends Message +final case class FailureMsg(error: String) extends Message diff --git a/src/main/scala/com/fluency03/blockchain/api/package.scala b/src/main/scala/com/fluency03/blockchain/api/package.scala index 2b57685..6fdf96d 100644 --- a/src/main/scala/com/fluency03/blockchain/api/package.scala +++ b/src/main/scala/com/fluency03/blockchain/api/package.scala @@ -16,4 +16,7 @@ package object api { val PARENT_UP = "../" + + + } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala index 66c8080..f504c22 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala @@ -33,8 +33,8 @@ trait BlockRoutes extends RoutesSupport { entity(as[Block]) { block => val blockCreated: Future[Message] = (blocksActor ? CreateBlock(block)).mapTo[Message] onSuccess(blockCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } @@ -47,8 +47,8 @@ trait BlockRoutes extends RoutesSupport { delete { val blockDeleted: Future[Message] = (blocksActor ? DeleteBlock(hash)).mapTo[Message] onSuccess(blockDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala index 7da7ad7..753f7a6 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -31,16 +31,16 @@ trait BlockchainRoutes extends RoutesSupport { entity(as[JValue]) { _ => val blockchainCreated: Future[Message] = (blockchainActor ? CreateBlockchain).mapTo[Message] onSuccess(blockchainCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } ~ delete { val blockchainDeleted: Future[Message] = (blockchainActor ? DeleteBlockchain).mapTo[Message] onSuccess(blockchainDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala index a0a87d7..5001766 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala @@ -10,43 +10,41 @@ import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.directives.MethodDirectives.post import akka.http.scaladsl.server.directives.RouteDirectives.complete import com.fluency03.blockchain.Util._ -import com.fluency03.blockchain.api.Input +import com.fluency03.blockchain.api.{FailureMsg, Input, SuccessMsg} trait GenericRoutes extends RoutesSupport { lazy val log = Logging(system, classOf[GenericRoutes]) - - lazy val genericRoutes: Route = pathSingleSlash { get { - complete("Welcome to Blockchain in Scala!") + complete(SuccessMsg(SLOGAN)) } } ~ - pathPrefix("generic") { - path("toSha256") { + pathPrefix(GENERIC) { + path(TO_SHA256) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, in.content.toSha256)) } + entity(as[Input]) { in => complete( failsafeResp { in.content.toSha256 } ) } } } ~ - path("toBase64") { + path(TO_BASE64) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, in.content.toBase64)) } + entity(as[Input]) { in => complete( failsafeResp { in.content.toBase64 } ) } } } ~ - path("fromBase64") { + path(FROM_BASE64) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, fromBase64(in.content))) } + entity(as[Input]) { in => complete( failsafeResp { fromBase64(in.content) } ) } } } ~ - path("epoch-time") { + path(TO_EPOCH_TIME) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, epochTimeOf(in.content))) } + entity(as[Input]) { in => complete( failsafeResp { epochTimeOf(in.content).toString } ) } } } ~ - path("time-of-epoch") { + path(TIME_FROM_EPOCH) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, Instant.ofEpochSecond(in.content.toLong))) } + entity(as[Input]) { in => complete( failsafeResp { Instant.ofEpochSecond(in.content.toLong).toString } ) } } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala index 9e0d9ff..e885914 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala @@ -33,8 +33,8 @@ trait NetworkRoutes extends RoutesSupport { entity(as[PeerSimple]) { peer => val peerCreated: Future[Message] = (networkActor ? CreatePeer(peer.name)).mapTo[Message] onSuccess(peerCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } @@ -47,8 +47,8 @@ trait NetworkRoutes extends RoutesSupport { delete { val peerDeleted: Future[Message] = (networkActor ? DeletePeer(name)).mapTo[Message] onSuccess(peerDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala index a9dc8c0..64e8553 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala @@ -33,8 +33,8 @@ trait TransactionRoutes extends RoutesSupport { entity(as[Transaction]) { tx => val txCreated: Future[Message] = (transActor ? CreateTransaction(tx)).mapTo[Message] onSuccess(txCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } @@ -47,8 +47,8 @@ trait TransactionRoutes extends RoutesSupport { delete { val txDeleted: Future[Message] = (transActor ? DeleteTransaction(id)).mapTo[Message] onSuccess(txDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/package.scala b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala new file mode 100644 index 0000000..18ac220 --- /dev/null +++ b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala @@ -0,0 +1,16 @@ +package com.fluency03.blockchain.api + +package object routes { + + // generics + val SLASH = "/" + val GENERIC = "generic" + val TO_SHA256 = "to-sha256" + val TO_BASE64 = "to-base64" + val FROM_BASE64 = "from-base64" + val TO_EPOCH_TIME = "to-epoch-time" + val TIME_FROM_EPOCH = "time-from-epoch" + + def pathOf(seg: String*): String = SLASH + seg.mkString(SLASH) + +} diff --git a/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala b/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala index e8de957..fc6c8db 100644 --- a/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala @@ -6,7 +6,7 @@ class MessageTest extends FlatSpec with Matchers { "A Message" should "contain valid content." in { SuccessMsg("Response test.") shouldBe a[Message] - SuccessMsg("Response test.").content shouldEqual "Response test." + SuccessMsg("Response test.").message shouldEqual "Response test." } "A Input" should "contain valid content." in { @@ -15,7 +15,7 @@ class MessageTest extends FlatSpec with Matchers { "A Fail" should "contain valid content." in { FailureMsg("Error") shouldBe a[Message] - FailureMsg("Error").content shouldEqual "Error" + FailureMsg("Error").error shouldEqual "Error" } } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala index 1496b08..486af66 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala @@ -1,5 +1,8 @@ package com.fluency03.blockchain.api.routes -class BlockRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class BlockRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala index e4e2a6f..2b8a8a1 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala @@ -1,5 +1,12 @@ package com.fluency03.blockchain.api.routes -class BlockchainRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class BlockchainRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { + + + + } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala index b2c4a9f..7b3e445 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala @@ -1,5 +1,52 @@ package com.fluency03.blockchain.api.routes -class GenericRoutesTest { +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.testkit.ScalatestRouteTest +import com.fluency03.blockchain.api.{Input, JsonSupport, SuccessMsg} +import org.scalatest.{Matchers, WordSpec} +import com.fluency03.blockchain.api.Server.genericRoutes + +class GenericRoutesTest extends WordSpec with Matchers with ScalatestRouteTest with JsonSupport { + + "GenericRoutes" should { + "return a greeting for GET requests to the root path." in { + Get() ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("Welcome to Blockchain in Scala!") + } + } + + "return corresponding value." in { + Post(pathOf(GENERIC, TO_SHA256), Input("open sesame")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb") + } + + Post(pathOf(GENERIC, TO_SHA256), Input("")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + } + + Post(pathOf(GENERIC, TO_BASE64), Input("open sesame")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("b3BlbiBzZXNhbWU=") + } + + Post(pathOf(GENERIC, FROM_BASE64), Input("b3BlbiBzZXNhbWU=")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("open sesame") + } + + Post(pathOf(GENERIC, TO_EPOCH_TIME), Input("2018-04-11T18:52:01Z")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("1523472721") + } + + Post(pathOf(GENERIC, TIME_FROM_EPOCH), Input("1523472721")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("2018-04-11T18:52:01Z") + } + } + } } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala index ec99e99..f5f013d 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala @@ -1,5 +1,8 @@ package com.fluency03.blockchain.api.routes -class NetworkRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class NetworkRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala index fdaa75d..7788748 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala @@ -1,5 +1,8 @@ package com.fluency03.blockchain.api.routes -class TransactionRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class TransactionRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { } From b2b1a5faac955ed3091117382823eda9d2712ac7 Mon Sep 17 00:00:00 2001 From: fluency03 Date: Thu, 26 Apr 2018 12:28:55 +0100 Subject: [PATCH 2/2] remove Util object --- .../scala/com/fluency03/blockchain/Util.scala | 84 ------------------- .../fluency03/blockchain/api/package.scala | 3 +- .../blockchain/api/routes/BlockRoutes.scala | 4 +- .../api/routes/BlockchainRoutes.scala | 2 +- .../blockchain/api/routes/GenericRoutes.scala | 4 +- .../blockchain/api/routes/NetworkRoutes.scala | 4 +- .../api/routes/TransactionRoutes.scala | 4 +- .../blockchain/api/routes/package.scala | 43 ++++++++++ .../com/fluency03/blockchain/core/Block.scala | 1 - .../blockchain/core/BlockHeader.scala | 1 - .../blockchain/core/Blockchain.scala | 3 - .../fluency03/blockchain/core/Merkle.scala | 2 - .../blockchain/core/Transaction.scala | 1 - .../com/fluency03/blockchain/package.scala | 59 ++++++++++++- .../com/fluency03/blockchain/UtilTest.scala | 1 - .../blockchain/core/MerkleTest.scala | 1 - 16 files changed, 110 insertions(+), 107 deletions(-) delete mode 100644 src/main/scala/com/fluency03/blockchain/Util.scala diff --git a/src/main/scala/com/fluency03/blockchain/Util.scala b/src/main/scala/com/fluency03/blockchain/Util.scala deleted file mode 100644 index 8eb9b2a..0000000 --- a/src/main/scala/com/fluency03/blockchain/Util.scala +++ /dev/null @@ -1,84 +0,0 @@ -package com.fluency03.blockchain - -import java.security.MessageDigest -import java.time.Instant - -import akka.http.scaladsl.model.{StatusCode, StatusCodes} -import com.fluency03.blockchain.api.{FailureMsg, Message, SuccessMsg} -import org.bouncycastle.util.encoders.Base64 - -object Util { - - /** - * Generate SHA256 Hash from a input String. - * https://gist.github.com/navicore/6234040bbfce3aa58f866db314c07c15 - */ - def sha256HashOf(text: String) : String = String.format("%064x", - new java.math.BigInteger(1, digestOf(text))) - - /** - * Generate digest from a input String. - * https://gist.github.com/navicore/6234040bbfce3aa58f866db314c07c15 - */ - def digestOf(text: String): Bytes = - MessageDigest.getInstance("SHA-256").digest(text.getBytes("UTF-8")) - - /** - * Return the current timestamp in Unix Epoch Time. - */ - def getCurrentTimestamp: Long = Instant.now.getEpochSecond - - /** - * Parse a time format string to its Epoch time in seconds. - */ - def epochTimeOf(t: String): Long = Instant.parse(t).getEpochSecond - - /** - * Calculate the hash of concatenation a Seq of Strings. - */ - def sha256Of(strings: String*): String = sha256HashOf(strings mkString "") - - /** - * Get binary representation of a hash. - */ - def binaryOfHex(hash: String): String = BigInt(hash, 16).toString(2) - - /** - * Check whether the given hash is with valid difficulty. - */ - def isWithValidDifficulty(hash: String, difficulty: Int): Boolean = hash startsWith ("0" * difficulty) - - /** - * Encode a String to Base64. - */ - def base64Of(text: String): String = Base64.toBase64String(text.getBytes("UTF-8")) - - /** - * Encode an Array of Bytes String to Base64. - */ - def base64Of(data: Bytes): String = Base64.toBase64String(data) - - /** - * Decode a Base64 to String. - */ - def fromBase64(base64: String): String = new String(Base64.decode(base64), "UTF-8") - - /** - * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). - */ - def failsafeMsg(fun: => String): Message = - try { SuccessMsg(fun) } - catch { - case e: Exception => FailureMsg(e.getMessage) - } - - /** - * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). - */ - def failsafeResp(fun: => String): (StatusCode, Message) = - try { (StatusCodes.OK, SuccessMsg(fun)) } - catch { - case e: Exception => (StatusCodes.InternalServerError, FailureMsg(e.getMessage)) - } - -} diff --git a/src/main/scala/com/fluency03/blockchain/api/package.scala b/src/main/scala/com/fluency03/blockchain/api/package.scala index 6fdf96d..61857c2 100644 --- a/src/main/scala/com/fluency03/blockchain/api/package.scala +++ b/src/main/scala/com/fluency03/blockchain/api/package.scala @@ -1,5 +1,6 @@ package com.fluency03.blockchain +import akka.http.scaladsl.model.{StatusCode, StatusCodes} import com.fluency03.blockchain.core.{Block, Peer, Transaction} package object api { @@ -17,6 +18,4 @@ package object api { val PARENT_UP = "../" - - } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala index f504c22..214ffef 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala @@ -21,13 +21,13 @@ trait BlockRoutes extends RoutesSupport { def blocksActor: ActorRef lazy val blockRoutes: Route = - path("blocks") { + path(BLOCKS) { get { val blocks: Future[Blocks] = (blocksActor ? GetBlocks).mapTo[Blocks] complete(blocks) } } ~ - pathPrefix("block") { + pathPrefix(BLOCK) { pathEnd { post { entity(as[Block]) { block => diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala index 753f7a6..1e03b8f 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -21,7 +21,7 @@ trait BlockchainRoutes extends RoutesSupport { def blockchainActor: ActorRef lazy val blockchainRoutes: Route = - pathPrefix("blockchain") { + pathPrefix(BLOCKCHAIN) { pathEnd { get { val blockchain: Future[Option[Blockchain]] = (blockchainActor ? GetBlockchain).mapTo[Option[Blockchain]] diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala index 5001766..46cc9b3 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala @@ -4,13 +4,11 @@ package api.routes import java.time.Instant import akka.event.Logging -import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.directives.MethodDirectives.post import akka.http.scaladsl.server.directives.RouteDirectives.complete -import com.fluency03.blockchain.Util._ -import com.fluency03.blockchain.api.{FailureMsg, Input, SuccessMsg} +import com.fluency03.blockchain.api.{Input, SuccessMsg} trait GenericRoutes extends RoutesSupport { lazy val log = Logging(system, classOf[GenericRoutes]) diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala index e885914..70c4267 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala @@ -21,13 +21,13 @@ trait NetworkRoutes extends RoutesSupport { def networkActor: ActorRef lazy val networkRoutes: Route = - path("peers") { + path(PEERS) { get { val peers: Future[Set[String]] = (networkActor ? GetPeers).mapTo[Set[String]] complete(peers) } } ~ - pathPrefix("peer") { + pathPrefix(PEER) { pathEnd { post { entity(as[PeerSimple]) { peer => diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala index 64e8553..17055b6 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala @@ -21,13 +21,13 @@ trait TransactionRoutes extends RoutesSupport { def transActor: ActorRef lazy val transRoutes: Route = - path("transactions") { + path(TRANSACTIONS) { get { val transactions: Future[Transactions] = (transActor ? GetTransactions).mapTo[Transactions] complete(transactions) } } ~ - pathPrefix("transaction") { + pathPrefix(TRANSACTION) { pathEnd { post { entity(as[Transaction]) { tx => diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/package.scala b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala index 18ac220..4bdc032 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/package.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala @@ -1,5 +1,7 @@ package com.fluency03.blockchain.api +import akka.http.scaladsl.model.{StatusCode, StatusCodes} + package object routes { // generics @@ -11,6 +13,47 @@ package object routes { val TO_EPOCH_TIME = "to-epoch-time" val TIME_FROM_EPOCH = "time-from-epoch" + // blockchain + val BLOCKCHAIN = "blockchain" + + // block + val BLOCKS = "blocks" + val BLOCK = "block" + + // network + val PEERS = "peers" + val PEER = "peer" + + // transaction + val TRANSACTIONS = "transactions" + val TRANSACTION = "transaction" + + /** + * Concatenate a seq of String segments into full API path + */ def pathOf(seg: String*): String = SLASH + seg.mkString(SLASH) + /** + * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). + */ + def failsafeMsg(fun: => String): Message = + try { SuccessMsg(fun) } + catch { + case e: Exception => FailureMsg(e.getMessage) + } + + /** + * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). + */ + def failsafeResp(fun: => String): (StatusCode, Message) = + try { (StatusCodes.OK, SuccessMsg(fun)) } + catch { + case e: Exception => (StatusCodes.InternalServerError, FailureMsg(e.getMessage)) + } + + + + + + } diff --git a/src/main/scala/com/fluency03/blockchain/core/Block.scala b/src/main/scala/com/fluency03/blockchain/core/Block.scala index 4907884..de84707 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Block.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Block.scala @@ -1,7 +1,6 @@ package com.fluency03.blockchain package core -import com.fluency03.blockchain.Util.isWithValidDifficulty import com.fluency03.blockchain.core.BlockHeader.hashOfHeaderFields import com.fluency03.blockchain.core.Transaction.createCoinbaseTx import org.json4s.JsonAST.JObject diff --git a/src/main/scala/com/fluency03/blockchain/core/BlockHeader.scala b/src/main/scala/com/fluency03/blockchain/core/BlockHeader.scala index fb412b4..24135fa 100644 --- a/src/main/scala/com/fluency03/blockchain/core/BlockHeader.scala +++ b/src/main/scala/com/fluency03/blockchain/core/BlockHeader.scala @@ -2,7 +2,6 @@ package com.fluency03.blockchain package core import com.fluency03.blockchain.core.BlockHeader.hashOfBlockHeader -import com.fluency03.blockchain.Util.sha256Of import org.json4s.native.JsonMethods.{compact, render} import org.json4s.{Extraction, JValue} diff --git a/src/main/scala/com/fluency03/blockchain/core/Blockchain.scala b/src/main/scala/com/fluency03/blockchain/core/Blockchain.scala index 8330244..4ee74f5 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Blockchain.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Blockchain.scala @@ -1,12 +1,9 @@ package com.fluency03.blockchain package core -import com.fluency03.blockchain.Util.getCurrentTimestamp import com.fluency03.blockchain.core.Blockchain._ import com.fluency03.blockchain.core.Block.canBeChained -import scala.collection.mutable - /** * Blockchain with difficulty and the chain of Blocks. * @param difficulty Difficulty of a Blockchain diff --git a/src/main/scala/com/fluency03/blockchain/core/Merkle.scala b/src/main/scala/com/fluency03/blockchain/core/Merkle.scala index 3fa4d6a..be39da6 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Merkle.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Merkle.scala @@ -1,8 +1,6 @@ package com.fluency03.blockchain package core -import com.fluency03.blockchain.Util.sha256Of - object Merkle { def computeRoot(trans: Seq[Transaction]): String = computeRootOfHashes(trans.map(_.id)) diff --git a/src/main/scala/com/fluency03/blockchain/core/Transaction.scala b/src/main/scala/com/fluency03/blockchain/core/Transaction.scala index ac4fe2c..59e6e82 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Transaction.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Transaction.scala @@ -4,7 +4,6 @@ package core import java.security.KeyPair import com.fluency03.blockchain.Crypto -import com.fluency03.blockchain.Util.sha256Of import com.fluency03.blockchain.core.Transaction.hashOfTransaction import org.bouncycastle.util.encoders.Hex import org.json4s.JsonAST.JObject diff --git a/src/main/scala/com/fluency03/blockchain/package.scala b/src/main/scala/com/fluency03/blockchain/package.scala index 9cb55ce..5b77def 100644 --- a/src/main/scala/com/fluency03/blockchain/package.scala +++ b/src/main/scala/com/fluency03/blockchain/package.scala @@ -1,9 +1,9 @@ package com.fluency03 import java.nio.charset.Charset +import java.security.MessageDigest import java.time.Instant -import com.fluency03.blockchain.Util.{sha256HashOf, base64Of, binaryOfHex} import org.bouncycastle.util.encoders.{Base64, Hex} import org.json4s.{Formats, NoTypeHints} import org.json4s.native.Serialization @@ -41,4 +41,61 @@ package object blockchain { def toBase64: String = base64Of(bytes) } + + /** + * Generate SHA256 Hash from a input String. + * https://gist.github.com/navicore/6234040bbfce3aa58f866db314c07c15 + */ + def sha256HashOf(text: String) : String = String.format("%064x", + new java.math.BigInteger(1, digestOf(text))) + + /** + * Generate digest from a input String. + * https://gist.github.com/navicore/6234040bbfce3aa58f866db314c07c15 + */ + def digestOf(text: String): Bytes = + MessageDigest.getInstance("SHA-256").digest(text.getBytes("UTF-8")) + + /** + * Calculate the hash of concatenation a Seq of Strings. + */ + def sha256Of(strings: String*): String = sha256HashOf(strings mkString "") + + /** + * Return the current timestamp in Unix Epoch Time. + */ + def getCurrentTimestamp: Long = Instant.now.getEpochSecond + + /** + * Parse a time format string to its Epoch time in seconds. + */ + def epochTimeOf(t: String): Long = Instant.parse(t).getEpochSecond + + /** + * Get binary representation of a hash. + */ + def binaryOfHex(hash: String): String = BigInt(hash, 16).toString(2) + + /** + * Check whether the given hash is with valid difficulty. + */ + def isWithValidDifficulty(hash: String, difficulty: Int): Boolean = hash startsWith ("0" * difficulty) + + /** + * Encode a String to Base64. + */ + def base64Of(text: String): String = Base64.toBase64String(text.getBytes("UTF-8")) + + /** + * Encode an Array of Bytes String to Base64. + */ + def base64Of(data: Bytes): String = Base64.toBase64String(data) + + /** + * Decode a Base64 to String. + */ + def fromBase64(base64: String): String = new String(Base64.decode(base64), "UTF-8") + + + } diff --git a/src/test/scala/com/fluency03/blockchain/UtilTest.scala b/src/test/scala/com/fluency03/blockchain/UtilTest.scala index 4468ea5..3224542 100644 --- a/src/test/scala/com/fluency03/blockchain/UtilTest.scala +++ b/src/test/scala/com/fluency03/blockchain/UtilTest.scala @@ -2,7 +2,6 @@ package com.fluency03.blockchain import java.time.format.DateTimeParseException -import com.fluency03.blockchain.Util._ import org.scalamock.scalatest.MockFactory import org.scalatest.{FlatSpec, Matchers} diff --git a/src/test/scala/com/fluency03/blockchain/core/MerkleTest.scala b/src/test/scala/com/fluency03/blockchain/core/MerkleTest.scala index 0cf2a8c..7a5c20a 100644 --- a/src/test/scala/com/fluency03/blockchain/core/MerkleTest.scala +++ b/src/test/scala/com/fluency03/blockchain/core/MerkleTest.scala @@ -1,7 +1,6 @@ package com.fluency03.blockchain package core -import com.fluency03.blockchain.Util.sha256Of import com.fluency03.blockchain.core.Merkle._ import com.fluency03.blockchain.core.Transaction.createCoinbaseTx import org.scalatest.{FlatSpec, Matchers}