Skip to content

Commit

Permalink
add GetBlock for BlockchainActor & add UpdateTransaction for Transact…
Browse files Browse the repository at this point in the history
…ionsActor
  • Loading branch information
fluency03 committed Apr 29, 2018
1 parent 209a793 commit 88e11b8
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand All @@ -31,6 +32,7 @@ class BlockchainActor extends ActorSupport {
case GetBlockchain => onGetBlockchain()
case CreateBlockchain => onCreateBlockchain()
case DeleteBlockchain => onDeleteBlockchain()
case GetBlock(hash) => onGetBlock(hash)
case _ => unhandled _
}

Expand All @@ -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
}
}


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

Expand All @@ -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]
}

Expand All @@ -31,7 +32,6 @@ class TransactionsActor extends ActorSupport {

/**
* TODO (Chang):
* - Update transaction
* - Sign transaction
*
*/
Expand All @@ -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 _
}

Expand All @@ -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")
}


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

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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
Expand Down Expand Up @@ -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 }
}
}
} ~
Expand All @@ -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 }
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down Expand Up @@ -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."))

Expand Down

0 comments on commit 88e11b8

Please sign in to comment.