diff --git a/README.md b/README.md index 2083c46..040f8a4 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,11 @@ Inspired by: - [Naivecoin](https://github.com/lhartikk/naivecoin) of [@lhartikk](https://github.com/lhartikk) REST API service is built on the open source projects [akka](https://github.com/akka/akka) and [akka-http](https://github.com/akka/akka-http). + +API Documentation on Postman: [blockchain-in-scala](https://documenter.getpostman.com/view/1231202/blockchain-in-scala/RVu8iTUP) + +Start the service, run this Class: + +``` +com.fluency03.blockchain.api.Server +``` diff --git a/build.sbt b/build.sbt index ebd2f83..5e54ee7 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ organization := "com.fluency03" name := "blockchain-in-scala" -version := "0.0.2" +version := "0.0.6" scalaVersion := "2.12.0" @@ -43,3 +43,6 @@ libraryDependencies ++= { "org.bouncycastle" % "bcprov-jdk15on" % "1.59" ) } ++ httpDependencies ++ testDependencies + + +mainClass in assembly := Some("com.fluency03.blockchain.api.Server") diff --git a/project/plugins.sbt b/project/plugins.sbt index 5c39736..b34f37d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,3 +6,4 @@ addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") addSbtPlugin("com.artima.supersafe" %% "sbtplugin" % "1.1.3") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.4") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6") 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 2c0f293..99de84c 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,9 @@ 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.Blockchain +import com.fluency03.blockchain.core.{Block, Blockchain} + +import scala.collection.mutable object BlockchainActor { final case object GetBlockchain @@ -23,6 +25,7 @@ class BlockchainActor extends ActorSupport { // TODO (Chang): need persistence var blockchainOpt: Option[Blockchain] = None + var hashIndexMapping = mutable.Map.empty[String, Int] def receive: Receive = { case GetBlockchain => onGetBlockchain() @@ -31,6 +34,16 @@ class BlockchainActor extends ActorSupport { case _ => unhandled _ } + /** + * TODO (Chang): new APIS: + * - AddBlockOnBlockchain + * - GetBlockFromBlockchain + * - CheckBlockchainIsValid + * - GetTransactionOfABlock + * - MineNextBlock + * + */ + private def onGetBlockchain(): Unit = sender() ! blockchainOpt private def onCreateBlockchain(): Unit = @@ -46,8 +59,5 @@ class BlockchainActor extends ActorSupport { sender() ! SuccessMsg(s"Blockchain deleted.") } else sender() ! FailureMsg(s"Blockchain does not exist.") - // TODO (Chang): APIs for adding new Block on the chain - - } 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 d03a204..a10b252 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/BlocksActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/BlocksActor.scala @@ -9,6 +9,7 @@ import scala.collection.mutable object BlocksActor { final case object GetBlocks + final case class GetBlocks(hashes: Set[String]) final case class CreateBlock(block: Block) final case class GetBlock(hash: String) final case class DeleteBlock(hash: String) @@ -25,35 +26,48 @@ class BlocksActor extends ActorSupport { val transActor: ActorSelection = context.actorSelection(PARENT_UP + TRANS_ACTOR_NAME) // TODO (Chang): need persistence - var blocks = mutable.Map.empty[String, Block] + var blocksPool = mutable.Map.empty[String, Block] def receive: Receive = { case GetBlocks => onGetBlocks() + case GetBlocks(hashes) => onGetBlocks(hashes) case CreateBlock(block) => onCreateBlock(block) case GetBlock(hash) => onGetBlock(hash) case DeleteBlock(hash) => onDeleteBlock(hash) case _ => unhandled _ } - private[this] def onGetBlocks(): Unit = sender() ! blocks.values.toSeq + /** + * TODO (Chang): new APIS: + * - CreateBlock + * - GetBlock (onChain or offChain) + * - GetTransactionOfABlock + * - AddBlockOnChain + * + */ + + private[this] def onGetBlocks(): Unit = sender() ! blocksPool.values.toSeq + + private[this] def onGetBlocks(hashes: Set[String]): Unit = sender() ! blocksPool.filterKeys( + k => hashes.contains(k) + ).values.toSeq private[this] def onCreateBlock(block: Block): Unit = { - if (blocks.contains(block.hash)) sender() ! FailureMsg(s"Block ${block.hash} already exists.") + if (blocksPool.contains(block.hash)) sender() ! FailureMsg(s"Block ${block.hash} already exists.") else { - blocks += (block.hash -> block) + blocksPool += (block.hash -> block) sender() ! SuccessMsg(s"Block ${block.hash} created.") } } - private[this] def onGetBlock(hash: String): Unit = sender() ! blocks.get(hash) + private[this] def onGetBlock(hash: String): Unit = sender() ! blocksPool.get(hash) private[this] def onDeleteBlock(hash: String): Unit = { - if (blocks.contains(hash)) { - blocks -= hash + if (blocksPool.contains(hash)) { + blocksPool -= hash sender() ! SuccessMsg(s"Block $hash deleted.") } else sender() ! FailureMsg(s"Block $hash does not exist.") } - } diff --git a/src/main/scala/com/fluency03/blockchain/api/actors/NetworkActor.scala b/src/main/scala/com/fluency03/blockchain/api/actors/NetworkActor.scala index 3b0b838..c953beb 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/NetworkActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/NetworkActor.scala @@ -7,8 +7,12 @@ import com.fluency03.blockchain.api.actors.NetworkActor._ import com.fluency03.blockchain.api.actors.PeerActor.GetPublicKeys import com.fluency03.blockchain.core.Peer +import scala.concurrent.Future + object NetworkActor { + final case object GetNetwork final case object GetPeers + final case class GetPeers(names: Set[String]) final case class CreatePeer(name: String) final case class GetPeer(name: String) final case class DeletePeer(name: String) @@ -28,15 +32,33 @@ class NetworkActor extends ActorSupport { // TODO (Chang): need persistence + /** + * TODO (Chang): + * - Remove wallet + * - Sign transaction + * + */ + def receive: Receive = { + case GetNetwork => onGetNetwork() case GetPeers => onGetPeers() + case GetPeers(names) => onGetPeers(names) case CreatePeer(name) => onCreatePeer(name) case GetPeer(name) => onGetPeer(name) case DeletePeer(name) => onDeletePeer(name) case _ => unhandled _ } - private def onGetPeers(): Unit = sender() ! context.children.map(_.path.name).toSet + private def onGetNetwork(): Unit = sender() ! context.children.map(_.path.name).toSet + + private def onGetPeers(): Unit = Future.sequence(context.children.map(p => { + (p ? GetPublicKeys).mapTo[Set[String]].map(keys => p.path.name -> keys) + })).map(_.toMap).pipeTo(sender()) + + private def onGetPeers(names: Set[String]): Unit = Future.sequence(context.children + .filter(p => names.contains(p.path.name)) + .map(p => { (p ? GetPublicKeys).mapTo[Set[String]].map(keys => p.path.name -> keys) }) + ).map(_.toMap).pipeTo(sender()) private def onCreatePeer(name: String): Unit = if (context.child(name).isDefined) sender() ! FailureMsg(s"Peer $name has been created.") @@ -59,8 +81,4 @@ class NetworkActor extends ActorSupport { sender() ! SuccessMsg(s"Peer $name deleted.") } else sender() ! FailureMsg(s"Peer $name does not exist.") - // TODO (Chang): APIs for selecting Peers based on Seq of ids - - - } diff --git a/src/main/scala/com/fluency03/blockchain/api/actors/PeerActor.scala b/src/main/scala/com/fluency03/blockchain/api/actors/PeerActor.scala index bcde51b..92b1d2d 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/PeerActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/PeerActor.scala @@ -27,6 +27,13 @@ class PeerActor extends ActorSupport { val wallets = mutable.Map.empty[String, Wallet] val others = mutable.Map.empty[String, Peer] + /** + * TODO (Chang): + * - Remove wallet + * - Sign transactions + * + */ + def receive: Receive = { case GetPublicKeys => sender() ! wallets.values.map(_.address).toSet case CreateWallet => sender() ! addWallet() 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 d6e273d..1fc0699 100644 --- a/src/main/scala/com/fluency03/blockchain/api/actors/TransactionsActor.scala +++ b/src/main/scala/com/fluency03/blockchain/api/actors/TransactionsActor.scala @@ -9,9 +9,10 @@ import scala.collection.mutable object TransactionsActor { final case object GetTransactions + final case class GetTransactions(ids: Set[String]) final case class CreateTransaction(tx: Transaction) - final case class GetTransaction(hash: String) - final case class DeleteTransaction(hash: String) + final case class GetTransaction(id: String) + final case class DeleteTransaction(id: String) def props: Props = Props[TransactionsActor] } @@ -21,40 +22,49 @@ class TransactionsActor extends ActorSupport { override def postStop(): Unit = log.info("{} stopped!", this.getClass.getSimpleName) // TODO (Chang): need persistence - val currentTransactions: mutable.Map[String, Transaction] = mutable.Map.empty[String, Transaction] + val transPool: mutable.Map[String, Transaction] = mutable.Map.empty[String, Transaction] val uTxOs: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut] val blockchainActor: ActorSelection = context.actorSelection(PARENT_UP + BLOCKCHAIN_ACTOR_NAME) val blockActor: ActorSelection = context.actorSelection(PARENT_UP + BLOCKS_ACTOR_NAME) val networkActor: ActorSelection = context.actorSelection(PARENT_UP + NETWORK_ACTOR_NAME) + /** + * TODO (Chang): + * - Update transaction + * - Sign transaction + * + */ + def receive: Receive = { case GetTransactions => onGetTransactions() + case GetTransactions(ids) => onGetTransactions(ids) case CreateTransaction(tx) => onCreateTransaction(tx) - case GetTransaction(hash) => onGetTransaction(hash) - case DeleteTransaction(hash) => onDeleteTransaction(hash) + case GetTransaction(id) => onGetTransaction(id) + case DeleteTransaction(id) => onDeleteTransaction(id) case _ => unhandled _ } - private def onGetTransactions(): Unit = sender() ! currentTransactions.values.toSeq + private def onGetTransactions(): Unit = sender() ! transPool.values.toSeq + + private def onGetTransactions(ids: Set[String]): Unit = sender() ! transPool.filterKeys( + k => ids.contains(k) + ).values.toSeq private def onCreateTransaction(tx: Transaction): Unit = { - if (currentTransactions.contains(tx.id)) sender() ! FailureMsg(s"Transaction ${tx.id} already exists.") + if (transPool.contains(tx.id)) sender() ! FailureMsg(s"Transaction ${tx.id} already exists.") else { - currentTransactions += (tx.id -> tx) + transPool += (tx.id -> tx) sender() ! SuccessMsg(s"Transaction ${tx.id} created.") } } - private def onGetTransaction(id: String): Unit = sender() ! currentTransactions.get(id) + private def onGetTransaction(id: String): Unit = sender() ! transPool.get(id) private def onDeleteTransaction(id: String): Unit = - if (currentTransactions contains id) { - currentTransactions -= id + if (transPool contains id) { + transPool -= id sender() ! SuccessMsg(s"Transaction $id deleted.") } else sender() ! FailureMsg(s"Transaction $id does not exist.") - // TODO (Chang): APIs for selecting transactions based on Seq of ids - - } 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 172f9d8..d3b1351 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala @@ -2,14 +2,14 @@ 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.PathDirectives.path import akka.http.scaladsl.server.directives.RouteDirectives.complete +import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers.CsvSeq import akka.pattern.ask -import com.fluency03.blockchain.api.{Blocks, FailureMsg, Message, SuccessMsg} +import com.fluency03.blockchain.api.{Blocks, Message} import com.fluency03.blockchain.api.actors.BlocksActor._ import com.fluency03.blockchain.core.Block @@ -20,10 +20,21 @@ trait BlockRoutes extends RoutesSupport { def blocksActor: ActorRef + /** + * TODO (Chang): new APIS: + * - CreateBlock + * - GetBlock (onChain or offChain) + * - GetTransactionOfABlock + * - AddBlockOnChain + * + */ + lazy val blockRoutes: Route = path(BLOCKS) { - get { - val blocks: Future[Blocks] = (blocksActor ? GetBlocks).mapTo[Blocks] + parameters( 'hashes.as(CsvSeq[String]) ? ) { hashes => + val blocks: Future[Blocks] = + if (hashes.isDefined) (blocksActor ? GetBlocks(hashes.get.toSet)).mapTo[Blocks] + else (blocksActor ? GetBlocks).mapTo[Blocks] complete(blocks) } } ~ 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 fb0ef81..c32da2c 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -18,6 +18,16 @@ trait BlockchainRoutes extends RoutesSupport { def blockchainActor: ActorRef + /** + * TODO (Chang): new APIS: + * - AddBlockOnBlockchain + * - GetBlockFromBlockchain + * - CheckBlockchainIsValid + * - GetTransactionOfABlock + * - MineNextBlock + * + */ + lazy val blockchainRoutes: Route = pathPrefix(BLOCKCHAIN) { pathEnd { 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 64991df..d4f301a 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala @@ -2,15 +2,15 @@ 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.PathDirectives.path 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.NetworkActor.{CreatePeer, DeletePeer, GetPeer, GetPeers} -import com.fluency03.blockchain.api.{FailureMsg, Message, SuccessMsg} +import com.fluency03.blockchain.api.Message +import com.fluency03.blockchain.api.actors.NetworkActor._ import com.fluency03.blockchain.core.{Peer, PeerSimple} import scala.concurrent.Future @@ -20,11 +20,27 @@ trait NetworkRoutes extends RoutesSupport { def networkActor: ActorRef + /** + * TODO (Chang): + * - Sign transaction + * + */ + lazy val networkRoutes: Route = + path(NETWORK) { + get { + val network: Future[Set[String]] = (networkActor ? GetNetwork).mapTo[Set[String]] + complete(network) + } + } ~ path(PEERS) { get { - val peers: Future[Set[String]] = (networkActor ? GetPeers).mapTo[Set[String]] - complete(peers) + parameters( 'names.as(CsvSeq[String]) ? ) { names => + val peers: Future[Map[String, Set[String]]] = + if (names.isDefined) (networkActor ? GetPeers(names.get.toSet)).mapTo[Map[String, Set[String]]] + else (networkActor ? GetPeers).mapTo[Map[String, Set[String]]] + complete(peers) + } } } ~ pathPrefix(PEER) { @@ -41,10 +57,10 @@ trait NetworkRoutes extends RoutesSupport { val maybePeer: Future[Option[Peer]] = (networkActor ? GetPeer(name)).mapTo[Option[Peer]] rejectEmptyResponse { complete(maybePeer) } } ~ - delete { - val peerDeleted: Future[Message] = (networkActor ? DeletePeer(name)).mapTo[Message] - onSuccess(peerDeleted) { respondOnDeletion } - } + delete { + val peerDeleted: Future[Message] = (networkActor ? DeletePeer(name)).mapTo[Message] + onSuccess(peerDeleted) { respondOnDeletion } + } } } 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 3f241b2..4f63ace 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala @@ -2,15 +2,15 @@ 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.PathDirectives.path import akka.http.scaladsl.server.directives.RouteDirectives.complete +import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers.CsvSeq import akka.pattern.ask -import com.fluency03.blockchain.api.{FailureMsg, Message, SuccessMsg, Transactions} import com.fluency03.blockchain.api.actors.TransactionsActor._ +import com.fluency03.blockchain.api.{Message, Transactions} import com.fluency03.blockchain.core.Transaction import scala.concurrent.Future @@ -20,10 +20,19 @@ trait TransactionRoutes extends RoutesSupport { def transActor: ActorRef + /** + * TODO (Chang): + * - Update transaction + * - Sign transaction + * + */ + lazy val transRoutes: Route = path(TRANSACTIONS) { - get { - val transactions: Future[Transactions] = (transActor ? GetTransactions).mapTo[Transactions] + parameters( 'ids.as(CsvSeq[String]) ? ) { ids => + val transactions: Future[Transactions] = + if (ids.isDefined) (transActor ? GetTransactions(ids.get.toSet)).mapTo[Transactions] + else (transActor ? GetTransactions).mapTo[Transactions] complete(transactions) } } ~ 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 29d2cbd..8990774 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/package.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala @@ -21,6 +21,7 @@ package object routes { val BLOCK = "block" // network + val NETWORK = "network" val PEERS = "peers" val PEER = "peer" diff --git a/src/main/scala/com/fluency03/blockchain/core/Peer.scala b/src/main/scala/com/fluency03/blockchain/core/Peer.scala index 94e2f9b..69c24fb 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Peer.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Peer.scala @@ -1,7 +1,4 @@ package com.fluency03.blockchain.core -trait PeerSimple { - def name: String -} - -case class Peer(name: String, publicKeys: Set[String]) extends PeerSimple +case class PeerSimple(name: String) +case class Peer(name: String, publicKeys: Set[String]) diff --git a/src/main/scala/com/fluency03/blockchain/package.scala b/src/main/scala/com/fluency03/blockchain/package.scala index f5046cb..d947e31 100644 --- a/src/main/scala/com/fluency03/blockchain/package.scala +++ b/src/main/scala/com/fluency03/blockchain/package.scala @@ -5,10 +5,10 @@ import java.security.{MessageDigest, PrivateKey, PublicKey} import java.time.Instant import com.fluency03.blockchain.Crypto.{privateKeyToHex, publicKeyToHex} -import com.fluency03.blockchain.core.{TxIn, TxOut} +import com.fluency03.blockchain.core.{Peer, PeerSimple, TxIn, TxOut} import org.bouncycastle.util.encoders.{Base64, Hex} -import org.json4s.{Formats, NoTypeHints} import org.json4s.native.Serialization +import org.json4s.{Formats, NoTypeHints} import scala.io.Source @@ -52,6 +52,10 @@ package object blockchain { def toHex: String = privateKeyToHex(privateKey) } + implicit class PeerImplicit(val peer: Peer) { + def toSimple: PeerSimple = PeerSimple(peer.name) + } + /** * Generate SHA256 Hash from a input String. diff --git a/src/test/scala/com/fluency03/blockchain/api/actors/BlocksActorTest.scala b/src/test/scala/com/fluency03/blockchain/api/actors/BlocksActorTest.scala index 958751c..ee51ed5 100644 --- a/src/test/scala/com/fluency03/blockchain/api/actors/BlocksActorTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/actors/BlocksActorTest.scala @@ -23,6 +23,9 @@ class BlocksActorTest extends TestKit(ActorSystem("BlocksActorTest")) with Impli blocksActor ! GetBlocks expectMsg(Seq.empty[Block]) + blocksActor ! GetBlocks(Set("somehash")) + expectMsg(Seq.empty[Block]) + blocksActor ! CreateBlock(Block.genesisBlock) expectMsg(SuccessMsg(s"Block ${Block.genesisBlock.hash} created.")) @@ -32,6 +35,12 @@ class BlocksActorTest extends TestKit(ActorSystem("BlocksActorTest")) with Impli blocksActor ! GetBlocks expectMsg(Seq(Block.genesisBlock)) + blocksActor ! GetBlocks(Set("somehash")) + expectMsg(Seq.empty[Block]) + + blocksActor ! GetBlocks(Set(Block.genesisBlock.hash)) + expectMsg(Seq(Block.genesisBlock)) + blocksActor ! GetBlock(Block.genesisBlock.hash) expectMsg(Some(Block.genesisBlock)) diff --git a/src/test/scala/com/fluency03/blockchain/api/actors/NetworkActorTest.scala b/src/test/scala/com/fluency03/blockchain/api/actors/NetworkActorTest.scala index 7b4116a..2e78763 100644 --- a/src/test/scala/com/fluency03/blockchain/api/actors/NetworkActorTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/actors/NetworkActorTest.scala @@ -20,9 +20,15 @@ class NetworkActorTest extends TestKit(ActorSystem("NetworkActorTest")) with Imp "Respond with a Set of Peers." in { NetworkActor.props shouldEqual Props[NetworkActor] - networkActor ! GetPeers + networkActor ! GetNetwork expectMsg(Set.empty[String]) + networkActor ! GetPeers + expectMsg(Map.empty[String, Set[String]]) + + networkActor ! GetPeers(Set("somename")) + expectMsg(Map.empty[String, Set[String]]) + val name = "peer" networkActor ! CreatePeer(name) expectMsg(SuccessMsg(s"Peer $name created.")) @@ -30,9 +36,22 @@ class NetworkActorTest extends TestKit(ActorSystem("NetworkActorTest")) with Imp networkActor ! CreatePeer(name) expectMsg(FailureMsg(s"Peer $name has been created.")) - networkActor ! GetPeers + networkActor ! GetNetwork expectMsg(Set(name)) + networkActor ! GetPeers + val peers = expectMsgType[Map[String, Set[String]]] + peers.size shouldEqual 1 + peers.contains(name) shouldEqual true + peers(name).size shouldEqual 1 + + networkActor ! GetPeers(Set("somename")) + expectMsg(Map.empty[String, Set[String]]) + + networkActor ! GetPeers(Set(name)) + val peers2 = expectMsgType[Map[String, Set[String]]] + peers2 shouldEqual peers + networkActor ! GetPeer(name) val peerOpt = expectMsgType[Some[Peer]] peerOpt.isDefined shouldEqual true 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 5bc716d..d0162e6 100644 --- a/src/test/scala/com/fluency03/blockchain/api/actors/TransactionsActorTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/actors/TransactionsActorTest.scala @@ -25,6 +25,9 @@ class TransactionsActorTest extends TestKit(ActorSystem("TransactionsActorTest") transActor ! GetTransactions expectMsg(Seq.empty[Transaction]) + transActor ! GetTransactions(Set("someid")) + expectMsg(Seq.empty[Transaction]) + val genesisTx: Transaction = createCoinbaseTx(0, genesisMiner, genesisTimestamp) transActor ! CreateTransaction(genesisTx) expectMsg(SuccessMsg(s"Transaction ${genesisTx.id} created.")) @@ -35,6 +38,12 @@ class TransactionsActorTest extends TestKit(ActorSystem("TransactionsActorTest") transActor ! GetTransactions expectMsg(Seq(genesisTx)) + transActor ! GetTransactions(Set("someid")) + expectMsg(Seq.empty[Transaction]) + + transActor ! GetTransactions(Set(genesisTx.id)) + expectMsg(Seq(genesisTx)) + transActor ! GetTransaction(genesisTx.id) expectMsg(Some(genesisTx)) 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 486af66..aff84b5 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala @@ -1,8 +1,9 @@ package com.fluency03.blockchain.api.routes import akka.http.scaladsl.testkit.ScalatestRouteTest +import com.fluency03.blockchain.api.JsonSupport import org.scalatest.{Matchers, WordSpec} -class BlockRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { +class BlockRoutesTest extends WordSpec with Matchers with ScalatestRouteTest with JsonSupport { } 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 2b8a8a1..ed3a460 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala @@ -1,9 +1,10 @@ package com.fluency03.blockchain.api.routes import akka.http.scaladsl.testkit.ScalatestRouteTest +import com.fluency03.blockchain.api.JsonSupport import org.scalatest.{Matchers, WordSpec} -class BlockchainRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { +class BlockchainRoutesTest extends WordSpec with Matchers with ScalatestRouteTest with JsonSupport { 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 f5f013d..28e9567 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala @@ -1,8 +1,9 @@ package com.fluency03.blockchain.api.routes import akka.http.scaladsl.testkit.ScalatestRouteTest +import com.fluency03.blockchain.api.JsonSupport import org.scalatest.{Matchers, WordSpec} -class NetworkRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { +class NetworkRoutesTest extends WordSpec with Matchers with ScalatestRouteTest with JsonSupport { } 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 7788748..6dbe8cc 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala @@ -1,8 +1,9 @@ package com.fluency03.blockchain.api.routes import akka.http.scaladsl.testkit.ScalatestRouteTest +import com.fluency03.blockchain.api.JsonSupport import org.scalatest.{Matchers, WordSpec} -class TransactionRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { +class TransactionRoutesTest extends WordSpec with Matchers with ScalatestRouteTest with JsonSupport { } diff --git a/src/test/scala/com/fluency03/blockchain/core/PeerTest.scala b/src/test/scala/com/fluency03/blockchain/core/PeerTest.scala index c95d101..619c589 100644 --- a/src/test/scala/com/fluency03/blockchain/core/PeerTest.scala +++ b/src/test/scala/com/fluency03/blockchain/core/PeerTest.scala @@ -8,8 +8,11 @@ class PeerTest extends FlatSpec with Matchers { val p = Peer("peer", Set("abcd")) p.name shouldEqual "peer" p.publicKeys shouldEqual Set("abcd") - p shouldBe a[PeerSimple] p shouldBe a[Peer] + p should not be a[PeerSimple] + val ps = p.toSimple + ps shouldBe a[PeerSimple] + ps.name shouldEqual "peer" } }