From 88e11b88716515d3a572e83290698e57e3fea4ac Mon Sep 17 00:00:00 2001 From: fluency03 Date: Mon, 30 Apr 2018 01:55:44 +0200 Subject: [PATCH] add GetBlock for BlockchainActor & add UpdateTransaction for TransactionsActor --- .../api/actors/BlockchainActor.scala | 21 +++++++++++++++++ .../api/actors/TransactionsActor.scala | 19 +++++++++++++-- .../api/routes/BlockchainRoutes.scala | 8 ++++++- .../blockchain/api/routes/RoutesSupport.scala | 5 ++++ .../api/routes/TransactionRoutes.scala | 23 ++++++++++++++++--- .../api/actors/BlockchainActorTest.scala | 8 ++++++- .../api/actors/TransactionsActorTest.scala | 16 +++++++++++-- 7 files changed, 91 insertions(+), 9 deletions(-) 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 99de84c..f377180 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/BlockchainActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/BlockchainActor.scala @@ -11,6 +11,7 @@ object BlockchainActor { final case object GetBlockchain final case object CreateBlockchain final case object DeleteBlockchain + final case class GetBlock(hash: String) def props: Props = Props[BlockchainActor] } @@ -31,6 +32,7 @@ class BlockchainActor extends ActorSupport { case GetBlockchain => onGetBlockchain() case CreateBlockchain => onCreateBlockchain() case DeleteBlockchain => onDeleteBlockchain() + case GetBlock(hash) => onGetBlock(hash) case _ => unhandled _ } @@ -50,14 +52,33 @@ class BlockchainActor extends ActorSupport { if (blockchainOpt.isDefined) sender() ! FailureMsg(s"Blockchain already exists.") else { blockchainOpt = Some(Blockchain()) + if (hashIndexMapping.nonEmpty) { + log.error("Hash-to-index mapping is not empty when Blockchain is created! Clear it!") + hashIndexMapping.clear() + } + blockchainOpt.get.chain.zipWithIndex.foreach { case (b, i) => hashIndexMapping += (b.hash -> i) } sender() ! SuccessMsg(s"Blockchain created, with difficulty ${blockchainOpt.get.difficulty}.") } private def onDeleteBlockchain(): Unit = if (blockchainOpt.isDefined) { blockchainOpt = None + hashIndexMapping.clear() sender() ! SuccessMsg(s"Blockchain deleted.") } else sender() ! FailureMsg(s"Blockchain does not exist.") + private def onGetBlock(hash: String): Unit = sender() ! { + 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/TransactionsActor.scala b/src/main/scala/com/fluency03/blockchain/api/actors/TransactionsActor.scala index 1fc0699..17d34f7 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/TransactionsActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/TransactionsActor.scala @@ -4,6 +4,7 @@ import akka.actor.{ActorSelection, Props} import com.fluency03.blockchain.api.actors.TransactionsActor._ import com.fluency03.blockchain.api._ import com.fluency03.blockchain.core.{Outpoint, Transaction, TxOut} +import com.fluency03.blockchain.core.Transaction.hashOfTransaction import scala.collection.mutable @@ -13,7 +14,7 @@ object TransactionsActor { final case class CreateTransaction(tx: Transaction) final case class GetTransaction(id: String) final case class DeleteTransaction(id: String) - + final case class UpdateTransaction(tx: Transaction) def props: Props = Props[TransactionsActor] } @@ -31,7 +32,6 @@ class TransactionsActor extends ActorSupport { /** * TODO (Chang): - * - Update transaction * - Sign transaction * */ @@ -42,6 +42,7 @@ class TransactionsActor extends ActorSupport { case CreateTransaction(tx) => onCreateTransaction(tx) case GetTransaction(id) => onGetTransaction(id) case DeleteTransaction(id) => onDeleteTransaction(id) + case UpdateTransaction(tx) => onUpdateTransaction(tx) case _ => unhandled _ } @@ -67,4 +68,18 @@ class TransactionsActor extends ActorSupport { sender() ! SuccessMsg(s"Transaction $id deleted.") } else sender() ! FailureMsg(s"Transaction $id does not exist.") + private def onUpdateTransaction(tx: Transaction): Unit = { + val actualId = tx.id + val expectedId = hashOfTransaction(tx) + if (actualId == expectedId) { + val notExistBefore = !transPool.contains(actualId) + transPool += (actualId -> tx) + sender() ! { + if (notExistBefore) SuccessMsg(s"Transaction $actualId does not exist. New transaction created.") + else SuccessMsg(s"Transaction $actualId updated.") + } + } else sender() ! FailureMsg(s"Transaction does not have valid ID. Should be: $expectedId; actually is: $actualId") + } + + } 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 c32da2c..3c39593 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -9,7 +9,7 @@ import akka.http.scaladsl.server.directives.RouteDirectives.complete import akka.pattern.ask import com.fluency03.blockchain.api.Message import com.fluency03.blockchain.api.actors.BlockchainActor._ -import com.fluency03.blockchain.core.Blockchain +import com.fluency03.blockchain.core.{Block, Blockchain} import scala.concurrent.Future @@ -44,5 +44,11 @@ trait BlockchainRoutes extends RoutesSupport { onSuccess(blockchainDeleted) { respondOnDeletion } } } + path(BLOCK / Segment) { hash => + get { + val maybeBlock: Future[Option[Block]] = (blockchainActor ? GetBlock(hash)).mapTo[Option[Block]] + rejectEmptyResponse { complete(maybeBlock) } + } + } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/RoutesSupport.scala b/src/main/scala/com/fluency03/blockchain/api/routes/RoutesSupport.scala index 8f0090c..9cc3b71 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/RoutesSupport.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/RoutesSupport.scala @@ -27,4 +27,9 @@ trait RoutesSupport extends JsonSupport { case f: FailureMsg => complete((StatusCodes.NotFound, f)) } + def respondOnUpdate(m: Message): StandardRoute = m match { + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.InternalServerError, 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 4f63ace..8e73a5c 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala @@ -2,6 +2,7 @@ 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} @@ -10,7 +11,7 @@ import akka.http.scaladsl.server.directives.RouteDirectives.complete import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers.CsvSeq import akka.pattern.ask import com.fluency03.blockchain.api.actors.TransactionsActor._ -import com.fluency03.blockchain.api.{Message, Transactions} +import com.fluency03.blockchain.api.{FailureMsg, Message, Transactions} import com.fluency03.blockchain.core.Transaction import scala.concurrent.Future @@ -40,8 +41,14 @@ trait TransactionRoutes extends RoutesSupport { pathEnd { post { entity(as[Transaction]) { tx => - val txCreated: Future[Message] = (transActor ? CreateTransaction(tx)).mapTo[Message] - onSuccess(txCreated) { respondOnCreation } + val msgOnCreate: Future[Message] = (transActor ? CreateTransaction(tx)).mapTo[Message] + onSuccess(msgOnCreate) { respondOnCreation } + } + } + put { + entity(as[Transaction]) { tx => + val msgOnUpdate: Future[Message] = (transActor ? UpdateTransaction(tx)).mapTo[Message] + onSuccess(msgOnUpdate) { respondOnUpdate } } } } ~ @@ -53,6 +60,16 @@ trait TransactionRoutes extends RoutesSupport { delete { val txDeleted: Future[Message] = (transActor ? DeleteTransaction(id)).mapTo[Message] onSuccess(txDeleted) { respondOnDeletion } + } ~ + put { + entity(as[Transaction]) { tx => + if (tx.id != id) + complete((StatusCodes.InternalServerError, FailureMsg("Transaction ID in the data does not match ID on the path."))) + else { + val msgOnUpdate: Future[Message] = (transActor ? UpdateTransaction(tx)).mapTo[Message] + onSuccess(msgOnUpdate) { respondOnUpdate } + } + } } } } diff --git a/src/test/scala/com/fluency03/blockchain/api/actors/BlockchainActorTest.scala b/src/test/scala/com/fluency03/blockchain/api/actors/BlockchainActorTest.scala index 7dacceb..2dd94d2 100644 --- a/src/test/scala/com/fluency03/blockchain/api/actors/BlockchainActorTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/actors/BlockchainActorTest.scala @@ -34,7 +34,13 @@ class BlockchainActorTest extends TestKit(ActorSystem("BlockchainActorTest")) wi expectMsgType[FailureMsg] blockchainActor ! GetBlockchain - expectMsgType[Some[Blockchain]] + val blockchain = expectMsgType[Some[Blockchain]].get + + blockchainActor ! GetBlock("somehash") + expectMsg(None) + + blockchainActor ! GetBlock(blockchain.chain.head.hash) + expectMsg(Some(blockchain.chain.head)) blockchainActor ! DeleteBlockchain expectMsg(SuccessMsg(s"Blockchain deleted.")) diff --git a/src/test/scala/com/fluency03/blockchain/api/actors/TransactionsActorTest.scala b/src/test/scala/com/fluency03/blockchain/api/actors/TransactionsActorTest.scala index d0162e6..1be093b 100644 --- a/src/test/scala/com/fluency03/blockchain/api/actors/TransactionsActorTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/actors/TransactionsActorTest.scala @@ -4,8 +4,8 @@ import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{ImplicitSender, TestKit} import com.fluency03.blockchain.api.actors.TransactionsActor._ import com.fluency03.blockchain.api.{FailureMsg, SuccessMsg} -import com.fluency03.blockchain.core.Transaction -import com.fluency03.blockchain.core.Transaction.createCoinbaseTx +import com.fluency03.blockchain.core.{Transaction, TxIn, TxOut} +import com.fluency03.blockchain.core.Transaction.{createCoinbaseTx, hashOfTransaction} import com.fluency03.blockchain.{genesisMiner, genesisTimestamp} import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} @@ -47,6 +47,18 @@ class TransactionsActorTest extends TestKit(ActorSystem("TransactionsActorTest") transActor ! GetTransaction(genesisTx.id) expectMsg(Some(genesisTx)) + transActor ! UpdateTransaction(genesisTx) + expectMsg(SuccessMsg(s"Transaction ${genesisTx.id} updated.")) + + val tx1: Transaction = createCoinbaseTx(1, genesisMiner, genesisTimestamp + 10) + transActor ! UpdateTransaction(tx1) + expectMsg(SuccessMsg(s"Transaction ${tx1.id} does not exist. New transaction created.")) + + val tx0: Transaction = Transaction(Seq.empty[TxIn], Seq.empty[TxOut], genesisTimestamp, "0000") + val idOfTx0 = hashOfTransaction(tx0) + transActor ! UpdateTransaction(tx0) + expectMsg(FailureMsg(s"Transaction does not have valid ID. Should be: $idOfTx0; actually is: ${tx0.id}")) + transActor ! DeleteTransaction(genesisTx.id) expectMsg(SuccessMsg(s"Transaction ${genesisTx.id} deleted."))