diff --git a/README.md b/README.md index 25e520d..5d444a6 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ com.fluency03.blockchain.api.Server #### Todos - Complete APIs' Todos +- API tests - Make states in actor persistent (using Akka Persistent) - Concurrent collections? - Make data distributed within cluster of peer (using Akka Cluster) diff --git a/src/main/scala/com/fluency03/blockchain/api/actors/BlockchainActor.scala b/src/main/scala/com/fluency03/blockchain/api/actors/BlockchainActor.scala index 34d1d9e..08c77f9 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/BlockchainActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/BlockchainActor.scala @@ -3,7 +3,7 @@ package com.fluency03.blockchain.api.actors import akka.actor.{ActorSelection, Props} import com.fluency03.blockchain.api.actors.BlockchainActor._ import com.fluency03.blockchain.api._ -import com.fluency03.blockchain.core.{Block, Blockchain} +import com.fluency03.blockchain.core.{Block, Blockchain, Transaction} import scala.collection.mutable @@ -13,6 +13,10 @@ object BlockchainActor { final case object DeleteBlockchain final case class GetBlock(hash: String) final case class GetTxOfBlock(id: String, hash: String) + final case class AddBlock(block: Block) + final case object RemoveBlock + final case class MineNextBlock(data: String, trans: Seq[Transaction]) + final case object CheckBlockchainValidity def props: Props = Props[BlockchainActor] } @@ -35,13 +39,15 @@ class BlockchainActor extends ActorSupport { case DeleteBlockchain => onDeleteBlockchain() case GetBlock(hash) => onGetBlock(hash) case GetTxOfBlock(id, hash) => onGetTxOfBlock(id, hash) + case AddBlock(block) => onAddBlock(block) + case RemoveBlock => onRemoveBlock() + case MineNextBlock(data, trans) => onMineNextBlock(data, trans) + case CheckBlockchainValidity => onCheckBlockchainValidity() case _ => unhandled _ } /** * TODO (Chang): new APIS: - * - AddBlockOnBlockchain - * - CheckBlockchainIsValid * - MineNextBlock * */ @@ -76,20 +82,57 @@ class BlockchainActor extends ActorSupport { } } - private def getBlock(hash: String): Option[Block] = { - hashIndexMapping.get(hash) match { - case Some(index) => blockchainOpt match { - case Some(blockchain) => Some(blockchain.chain(index)) - case None => - log.error("Blockchain does not exist! Clear the hash-to-index mapping!") - hashIndexMapping.clear() - None - } - case None => None - } + private def onAddBlock(block: Block): Unit = blockchainOpt match { + case Some(blockchain) => + blockchainOpt = Some(blockchain.addBlock(block)) + hashIndexMapping += (block.hash -> blockchain.length) + sender() ! SuccessMsg(s"New Block added on the chain.") + case None => + log.error("Blockchain does not exist! Clear the hash-to-index mapping!") + hashIndexMapping.clear() + sender() ! FailureMsg(s"Blockchain does not exist.") } + private def onRemoveBlock(): Unit = blockchainOpt match { + case Some(blockchain) => + val toBeRemoved = blockchain.chain.head + blockchainOpt = Some(blockchain.removeBlock()) + hashIndexMapping -= toBeRemoved.hash + sender() ! SuccessMsg(s"Last Block removed from the chain.") + case None => + log.error("Blockchain does not exist! Clear the hash-to-index mapping!") + hashIndexMapping.clear() + sender() ! FailureMsg(s"Blockchain does not exist.") + } + private def onMineNextBlock(data: String, trans: Seq[Transaction]): Unit = blockchainOpt match { + case Some(blockchain) => sender() ! Some(blockchain.mineNextBlock(data, trans)) + case None => + log.error("Blockchain does not exist! Clear the hash-to-index mapping!") + hashIndexMapping.clear() + sender() ! None + } + private def onCheckBlockchainValidity(): Unit = blockchainOpt match { + case Some(blockchain) => + if (blockchain.isValid) sender() ! SuccessMsg("true") + else sender() ! SuccessMsg("false") + case None => + log.error("Blockchain does not exist! Clear the hash-to-index mapping!") + hashIndexMapping.clear() + sender() ! FailureMsg(s"Blockchain does not exist.") + } + + + private def getBlock(hash: String): Option[Block] = hashIndexMapping.get(hash) match { + case Some(index) => blockchainOpt match { + case Some(blockchain) => Some(blockchain.chain(index)) + case None => + log.error("Blockchain does not exist! Clear the hash-to-index mapping!") + hashIndexMapping.clear() + None + } + case None => None + } } diff --git a/src/main/scala/com/fluency03/blockchain/api/actors/BlocksActor.scala b/src/main/scala/com/fluency03/blockchain/api/actors/BlocksActor.scala index a10b252..354b392 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/BlocksActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/BlocksActor.scala @@ -42,6 +42,7 @@ class BlocksActor extends ActorSupport { * - CreateBlock * - GetBlock (onChain or offChain) * - GetTransactionOfABlock + * - ContainsBlock * - AddBlockOnChain * */ @@ -69,5 +70,4 @@ class BlocksActor extends ActorSupport { } else sender() ! FailureMsg(s"Block $hash does not exist.") } - } 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 d3b1351..c9cd84c 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala @@ -25,6 +25,7 @@ trait BlockRoutes extends RoutesSupport { * - CreateBlock * - GetBlock (onChain or offChain) * - GetTransactionOfABlock + * - ContainsBlock * - AddBlockOnChain * */ @@ -58,4 +59,5 @@ trait BlockRoutes extends RoutesSupport { } } } + } 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 7d345a1..aff40cc 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -2,12 +2,13 @@ package com.fluency03.blockchain.api.routes import akka.actor.ActorRef 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.{delete, get, post} import akka.http.scaladsl.server.directives.RouteDirectives.complete import akka.pattern.ask -import com.fluency03.blockchain.api.Message +import com.fluency03.blockchain.api.{FailureMsg, Input, Message} import com.fluency03.blockchain.api.actors.BlockchainActor._ import com.fluency03.blockchain.core.{Block, Blockchain, Transaction} @@ -20,14 +21,18 @@ trait BlockchainRoutes extends RoutesSupport { /** * TODO (Chang): new APIS: - * - AddBlockOnBlockchain - * - CheckBlockchainIsValid * - MineNextBlock * */ lazy val blockchainRoutes: Route = pathPrefix(BLOCKCHAIN) { + path(VALIDITY) { + get { + val validity: Future[Message] = (blockchainActor ? CheckBlockchainValidity).mapTo[Message] + onSuccess(validity) { respondOnUpdate } + } + } ~ pathEnd { get { val blockchain: Future[Option[Blockchain]] = (blockchainActor ? GetBlockchain).mapTo[Option[Blockchain]] @@ -37,6 +42,25 @@ trait BlockchainRoutes extends RoutesSupport { val blockchainCreated: Future[Message] = (blockchainActor ? CreateBlockchain).mapTo[Message] onSuccess(blockchainCreated) { respondOnCreation } } ~ + put { + parameters('action) { + case ADD_BLOCK_ACTION => + entity(as[Block]) { block => + val blockchainUpdated: Future[Message] = (blockchainActor ? AddBlock(block)).mapTo[Message] + onSuccess(blockchainUpdated) { respondOnUpdate } + } + case REMOVE_BLOCK_ACTION => + val blockchainUpdated: Future[Message] = (blockchainActor ? RemoveBlock).mapTo[Message] + onSuccess(blockchainUpdated) { respondOnUpdate } + case MINE_NEXT_BLOCK_ACTION => + entity(as[Input]) { in => + val maybeNextBlock: Future[Option[Block]] = + (blockchainActor ? MineNextBlock(in.content, Seq.empty[Transaction])).mapTo[Option[Block]] + rejectEmptyResponse { complete(maybeNextBlock) } + } + case act => complete((StatusCodes.BadRequest, FailureMsg(s"Action Not Supported: $act"))) + } + } ~ delete { val blockchainDeleted: Future[Message] = (blockchainActor ? DeleteBlockchain).mapTo[Message] onSuccess(blockchainDeleted) { respondOnDeletion } @@ -46,9 +70,7 @@ trait BlockchainRoutes extends RoutesSupport { pathEnd { get { val maybeBlock: Future[Option[Block]] = (blockchainActor ? GetBlock(hash)).mapTo[Option[Block]] - rejectEmptyResponse { - complete(maybeBlock) - } + rejectEmptyResponse { complete(maybeBlock) } } } ~ path(TRANSACTION / Segment) { id => @@ -60,4 +82,5 @@ trait BlockchainRoutes extends RoutesSupport { } } } + } 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 8990774..2d87977 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/package.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala @@ -15,6 +15,10 @@ package object routes { // blockchain val BLOCKCHAIN = "blockchain" + val VALIDITY = "validity" + val ADD_BLOCK_ACTION = "addBlock" + val REMOVE_BLOCK_ACTION = "removeBlock" + val MINE_NEXT_BLOCK_ACTION = "mineNextBlock" // block val BLOCKS = "blocks"