Skip to content

Commit

Permalink
add Transaction APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed Apr 15, 2018
1 parent 4857d6c commit 831a195
Show file tree
Hide file tree
Showing 19 changed files with 300 additions and 185 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# blockchain-scala

Simple Blockchain Implementation in Scala.

Inspired by [Daniel van Flymen](http://www.dvf.nyc/)'s blog [Learn Blockchains by Building One](https://hackernoon.com/learn-blockchains-by-building-one-117428612f46), I started to implement this Scala version of a simple Blockchain.

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).

2 changes: 1 addition & 1 deletion scalastyle-config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
</check>
<check level="warning" class="org.scalastyle.scalariform.UppercaseLChecker" enabled="true"></check>
<check level="warning" class="org.scalastyle.scalariform.SimplifyBooleanExpressionChecker" enabled="true"></check>
<check level="warning" class="org.scalastyle.scalariform.IfBraceChecker" enabled="true">
<check level="warning" class="org.scalastyle.scalariform.IfBraceChecker" enabled="false">
<parameters>
<parameter name="singleLineAllowed"><![CDATA[true]]></parameter>
<parameter name="doubleLineAllowed"><![CDATA[false]]></parameter>
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
http {
host = "0.0.0.0"
port = 9090
}
24 changes: 14 additions & 10 deletions src/main/scala/com/fluency03/blockchain/api/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,38 @@ import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import com.fluency03.blockchain.api.actors.{BlockRegistryActor, BlockchainRegistryActor, TransactionRegistryActor}
import com.fluency03.blockchain.api.routes.{BlockRoutes, BlockchainRoutes}
import com.fluency03.blockchain.api.actors.{BlockActor, BlockchainActor, TransactionActor}
import com.fluency03.blockchain.api.routes.{BlockRoutes, BlockchainRoutes, TransactionRoutes}
import com.typesafe.config.ConfigFactory

import scala.concurrent.{Await, ExecutionContextExecutor, Future}
import scala.concurrent.duration.Duration

object Server extends App with BlockchainRoutes with BlockRoutes {
object Server extends App with BlockchainRoutes with BlockRoutes with TransactionRoutes {
implicit val system: ActorSystem = ActorSystem("blockchain-http-service")
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContextExecutor = system.dispatcher

override lazy val log = Logging(system, classOf[App])

val (interface, port) = (args(0), args(1).toInt)
val config = ConfigFactory.load()
val httpConfig = config.getConfig("http")
val (host, port) = (httpConfig.getString("host"), httpConfig.getInt("port"))

val blockchainRegistryActor: ActorRef = system.actorOf(BlockchainRegistryActor.props, "blockchainRegistryActor")
val blockRegistryActor: ActorRef = system.actorOf(BlockRegistryActor.props, "blockRegistryActor")
val blockchainActor: ActorRef = system.actorOf(BlockchainActor.props, BLOCKCHAIN_ACTOR_NAME)
val blockActor: ActorRef = system.actorOf(BlockActor.props, BLOCK_ACTOR_NAME)
val txActor: ActorRef = system.actorOf(TransactionActor.props, TX_ACTOR_NAME)

lazy val routes: Route = blockchainRoutes ~ blockRoutes
lazy val routes: Route = blockchainRoutes ~ blockRoutes ~ txRoutes

val bindingFuture: Future[ServerBinding] =
Http().bindAndHandle(routes, interface, port)
Http().bindAndHandle(routes, host, port)

bindingFuture.failed.foreach { ex =>
log.error(ex, "Failed to bind to {}:{}!", interface, port)
log.error(ex, "Failed to bind to {}:{}!", host, port)
}

log.info("Server online at http://{}:{}/", interface, port)
log.info("Server online at http://{}:{}/", host, port)

Await.result(system.whenTerminated, Duration.Inf)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.fluency03.blockchain.api.actors

import akka.actor.{Actor, ActorLogging, ActorSelection, Props}
import com.fluency03.blockchain.api.actors.BlockActor._
import com.fluency03.blockchain.api.utils.GenericMessage._
import com.fluency03.blockchain.api.{BLOCKCHAIN_ACTOR_NAME, PARENT_UP, TX_ACTOR_NAME}
import com.fluency03.blockchain.core.Block
import scala.collection.mutable

object BlockActor {
final case object GetBlocks
final case class CreateBlock(block: Block)
final case class GetBlock(hash: String)
final case class DeleteBlock(hash: String)

def props: Props = Props[BlockActor]
}

class BlockActor extends Actor with ActorLogging {
override def preStart(): Unit = log.info("{} started!", this.getClass.getSimpleName)
override def postStop(): Unit = log.info("{} stopped!", this.getClass.getSimpleName)

val blockchainActor: ActorSelection = context.actorSelection(PARENT_UP + BLOCKCHAIN_ACTOR_NAME)
val txActor: ActorSelection = context.actorSelection(PARENT_UP + TX_ACTOR_NAME)

var blocks = mutable.Map.empty[String, Block]

def receive: Receive = {
case GetBlocks => onGetBlocks()
case CreateBlock(block) => onCreateBlock(block)
case GetBlock(hash) => onGetBlock(hash)
case DeleteBlock(hash) => onDeleteBlock(hash)
}

private[this] def onGetBlocks(): Unit = sender() ! blocks.values.toList

private[this] def onCreateBlock(block: Block): Unit = {
blocks += (block.hash -> block)
sender() ! Response(s"Block ${block.hash} created.")
}

private[this] def onGetBlock(hash: String): Unit = sender() ! blocks.get(hash)

private[this] def onDeleteBlock(hash: String): Unit = {
blocks -= hash
sender() ! Response(s"Block $hash deleted.")
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.fluency03.blockchain.api.actors

import akka.actor.{Actor, ActorLogging, ActorSelection, Props}
import com.fluency03.blockchain.api.actors.BlockchainActor._
import com.fluency03.blockchain.api.actors.TransactionActor._
import com.fluency03.blockchain.api.utils.GenericMessage._
import com.fluency03.blockchain.api.{BLOCK_ACTOR_NAME, PARENT_UP, TX_ACTOR_NAME}
import com.fluency03.blockchain.core.{Blockchain, Transaction}

object BlockchainActor {
final case object GetBlockchain
final case object CreateBlockchain
final case object DeleteBlockchain

def props: Props = Props[BlockchainActor]
}

class BlockchainActor extends Actor with ActorLogging {
override def preStart(): Unit = log.info("{} started!", this.getClass.getSimpleName)
override def postStop(): Unit = log.info("{} stopped!", this.getClass.getSimpleName)

val txActor: ActorSelection = context.actorSelection(PARENT_UP + TX_ACTOR_NAME)
val blockActor: ActorSelection = context.actorSelection(PARENT_UP + BLOCK_ACTOR_NAME)

var blockchainOpt: Option[Blockchain] = None

def receive: Receive = {
/* ---------- Blockchain actions ---------- */
case GetBlockchain => onGetBlockchain()
case CreateBlockchain => onCreateBlockchain()
case DeleteBlockchain => onDeleteBlockchain()
/* ---------- Transaction actions ---------- */
case GetTransactions => onGetTransactions()
case CreateTransaction(tx) => onCreateTransaction(tx)
case GetTransaction(hash) => onGetTransaction(hash)
case DeleteTransaction(hash) => onDeleteTransaction(hash)
}

/* ---------- Blockchain actions ---------- */
private[this] def onGetBlockchain(): Unit = sender() ! blockchainOpt

private[this] def onCreateBlockchain(): Unit =
if (blockchainOpt.isDefined) sender() ! Response(s"Blockchain already exists.")
else {
blockchainOpt = Some(Blockchain())
sender() ! Response(s"Blockchain created, with difficulty ${blockchainOpt.get.difficulty}.")
}

private[this] def onDeleteBlockchain(): Unit =
if (blockchainOpt.isDefined) {
blockchainOpt = None
sender() ! Response(s"Blockchain deleted.")
} else sender() ! Response(s"Blockchain does not exist.")

/* ---------- Transaction actions ---------- */
private[this] def onGetTransactions(): Unit = sender() ! {
if (blockchainOpt.isDefined) blockchainOpt.get.currentTransactions.toList
else List()
}

private[this] def onCreateTransaction(tx: Transaction): Unit =
if (blockchainOpt.isDefined) {
blockchainOpt.get.currentTransactions += (tx.hash -> tx)
sender() ! Response(s"Transaction ${tx.hash} created.")
} else sender() ! Response(s"Blockchain does not exist.")

private[this] def onGetTransaction(hash: String): Unit = sender() ! {
if (blockchainOpt.isDefined) blockchainOpt.get.currentTransactions(hash)
else None
}

private[this] def onDeleteTransaction(hash: String): Unit =
if (blockchainOpt.isDefined) {
blockchainOpt.get.currentTransactions -= hash
sender() ! Response(s"Transaction $hash deleted.")
} else sender() ! Response(s"Blockchain does not exist.")

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.fluency03.blockchain.api.actors

import akka.actor.{Actor, ActorLogging, ActorSelection, Props}
import com.fluency03.blockchain.api.actors.TransactionActor._
import com.fluency03.blockchain.api.utils.GenericMessage._
import com.fluency03.blockchain.api.{BLOCKCHAIN_ACTOR_NAME, BLOCK_ACTOR_NAME, PARENT_UP}
import com.fluency03.blockchain.core.Transaction

object TransactionActor {
final case object GetTransactions
final case class CreateTransaction(tx: Transaction)
final case class GetTransaction(hash: String)
final case class DeleteTransaction(hash: String)

def props: Props = Props[TransactionActor]
}

class TransactionActor extends Actor with ActorLogging {
override def preStart(): Unit = log.info("{} started!", this.getClass.getSimpleName)
override def postStop(): Unit = log.info("{} stopped!", this.getClass.getSimpleName)

val blockchainActor: ActorSelection = context.actorSelection(PARENT_UP + BLOCKCHAIN_ACTOR_NAME)
val blockActor: ActorSelection = context.actorSelection(PARENT_UP + BLOCK_ACTOR_NAME)

def receive: Receive = {
case _ => blockchainActor forward _
}

//
// {
// case msg @ GetTransactions => blockchainActor forward msg
// case msg: CreateTransaction => blockchainActor forward msg
// case msg: GetTransaction => blockchainActor forward msg
// case msg: DeleteTransaction => blockchainActor forward msg
// }


}

This file was deleted.

9 changes: 8 additions & 1 deletion src/main/scala/com/fluency03/blockchain/api/package.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.fluency03.blockchain

import com.fluency03.blockchain.core.Block
import com.fluency03.blockchain.core.{Block, Transaction}

package object api {

type Blocks = List[Block]
type Transactions = List[Transaction]

lazy val BLOCK_ACTOR_NAME = "blockActor"
lazy val BLOCKCHAIN_ACTOR_NAME = "blockchainActor"
lazy val TX_ACTOR_NAME = "txActor"

lazy val PARENT_UP = "../"

}
Loading

0 comments on commit 831a195

Please sign in to comment.