Skip to content

Commit

Permalink
add timestamp to Transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed Apr 14, 2018
1 parent 32d889e commit 4857d6c
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 101 deletions.
6 changes: 3 additions & 3 deletions src/main/scala/com/fluency03/blockchain/api/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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}
import com.fluency03.blockchain.api.actors.{BlockRegistryActor, BlockchainRegistryActor, TransactionRegistryActor}
import com.fluency03.blockchain.api.routes.{BlockRoutes, BlockchainRoutes}

import scala.concurrent.{Await, ExecutionContextExecutor, Future}
Expand All @@ -18,7 +18,7 @@ object Server extends App with BlockchainRoutes with BlockRoutes {
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContextExecutor = system.dispatcher

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

val (interface, port) = (args(0), args(1).toInt)

Expand All @@ -34,7 +34,7 @@ object Server extends App with BlockchainRoutes with BlockRoutes {
log.error(ex, "Failed to bind to {}:{}!", interface, port)
}

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

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

import akka.actor.{Actor, ActorLogging, Props}
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import com.fluency03.blockchain.core.Block

object BlockRegistryActor {
Expand All @@ -10,22 +10,22 @@ object BlockRegistryActor {
final case class GetBlock(hash: String)
final case class DeleteBlock(hash: String)

def props: Props = Props[BlockRegistryActor]
def props: Props = Props[TransactionRegistryActor]
}

class BlockRegistryActor extends Actor with ActorLogging {
import BlockRegistryActor._

val blockchainActor: ActorRef = context.actorOf(BlockchainRegistryActor.props)

var blocks = Set.empty[Block]

def receive: Receive = {
case GetBlocks =>
sender() ! blocks.toList
case GetBlocks => sender() ! blocks.toList
case CreateBlock(block) =>
blocks += block
sender() ! ActionPerformed(s"Block ${block.hash} created.")
case GetBlock(hash) =>
sender() ! blocks.find(_.hash == hash)
case GetBlock(hash) => sender() ! blocks.find(_.hash == hash)
case DeleteBlock(hash) =>
blocks.find(_.hash == hash) foreach { block => blocks -= block }
sender() ! ActionPerformed(s"Block $hash deleted.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.fluency03.blockchain.api.actors

import akka.actor.{Actor, ActorLogging, Props}
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import com.fluency03.blockchain.core.Blockchain

object BlockchainRegistryActor {
Expand All @@ -15,11 +15,12 @@ object BlockchainRegistryActor {
class BlockchainRegistryActor extends Actor with ActorLogging {
import BlockchainRegistryActor._

val blockActor: ActorRef = context.actorOf(BlockRegistryActor.props)

var blockchainOpt: Option[Blockchain] = None

def receive: Receive = {
case GetBlockchain =>
sender() ! blockchainOpt.get
case GetBlockchain => sender() ! blockchainOpt.get
case CreateBlockchain =>
blockchainOpt = Some(Blockchain())
sender() ! ActionPerformed(s"Blockchain created, with difficulty ${blockchainOpt.get.difficulty}.")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.fluency03.blockchain.api.actors

import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import com.fluency03.blockchain.core.{Block, Transaction}

object TransactionRegistryActor {
final case class ActionPerformed(description: String)
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[TransactionRegistryActor]
}

class TransactionRegistryActor extends Actor with ActorLogging {
import TransactionRegistryActor._

var transactions = Set.empty[Transaction]

def receive: Receive = {
case GetTransactions => sender() ! transactions.toList
case CreateTransaction(tx) =>
transactions += tx
sender() ! ActionPerformed(s"Transaction ${tx.hash} created.")
case GetTransaction(hash) => sender() ! transactions.find(_.hash == hash)
case DeleteTransaction(hash) =>
transactions.find(_.hash == hash) foreach { tx => transactions -= tx }
sender() ! ActionPerformed(s"Transaction $hash deleted.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,42 @@ 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.pattern.ask
import com.fluency03.blockchain.api.JsonSupport
import com.fluency03.blockchain.api.actors.BlockRegistryActor._
import com.fluency03.blockchain.core.Block

import scala.concurrent.Future

trait BlockRoutes extends JsonSupport with Routes {
lazy val logBlockRoutes = Logging(system, classOf[BlockRoutes])
trait BlockRoutes extends Routes {
lazy val log = Logging(system, classOf[BlockRoutes])

def blockRegistryActor: ActorRef

lazy val blockRoutes: Route =
pathPrefix("blocks") {
pathEnd {
get {
val blocks: Future[List[Block]] =
(blockRegistryActor ? GetBlocks).mapTo[List[Block]]
val blocks: Future[List[Block]] = (blockRegistryActor ? GetBlocks).mapTo[List[Block]]
complete(blocks)
} ~
post {
entity(as[Block]) { block =>
val blockCreated: Future[ActionPerformed] =
(blockRegistryActor ? CreateBlock(block)).mapTo[ActionPerformed]
val blockCreated: Future[ActionPerformed] = (blockRegistryActor ? CreateBlock(block)).mapTo[ActionPerformed]
onSuccess(blockCreated) { performed =>
logBlockRoutes.info("Created user [{}]: {}", block.hash, performed.description)
log.info("Created user [{}]: {}", block.hash, performed.description)
complete((StatusCodes.Created, performed))
}
}
}
} ~
path(Segment) { hash =>
get {
val maybeBlock: Future[Option[Block]] =
(blockRegistryActor ? GetBlock(hash)).mapTo[Option[Block]]
rejectEmptyResponse {
complete(maybeBlock)
}
val maybeBlock: Future[Option[Block]] = (blockRegistryActor ? GetBlock(hash)).mapTo[Option[Block]]
rejectEmptyResponse { complete(maybeBlock) }
} ~
delete {
val blockDeleted: Future[ActionPerformed] =
(blockRegistryActor ? DeleteBlock(hash)).mapTo[ActionPerformed]
val blockDeleted: Future[ActionPerformed] = (blockRegistryActor ? DeleteBlock(hash)).mapTo[ActionPerformed]
onSuccess(blockDeleted) { performed =>
logBlockRoutes.info("Deleted block [{}]: {}", hash, performed.description)
log.info("Deleted block [{}]: {}", hash, performed.description)
complete((StatusCodes.OK, performed))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,30 @@ 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.JsonSupport
import com.fluency03.blockchain.api.actors.BlockchainRegistryActor._
import com.fluency03.blockchain.core.Blockchain
import org.json4s.JsonAST.JValue

import scala.concurrent.Future

trait BlockchainRoutes extends JsonSupport with Routes {
lazy val logBlockchainRoutes = Logging(system, classOf[BlockchainRoutes])
trait BlockchainRoutes extends Routes {
lazy val log = Logging(system, classOf[BlockchainRoutes])

def blockchainRegistryActor: ActorRef

lazy val blockchainRoutes: Route =
pathPrefix("blockchain") {
pathEnd {
get {
val users: Future[Blockchain] =
(blockchainRegistryActor ? GetBlockchain).mapTo[Blockchain]
val users: Future[Blockchain] = (blockchainRegistryActor ? GetBlockchain).mapTo[Blockchain]
complete(users)
} ~
post {
entity(as[JValue]) { _ =>
val blockchainCreated: Future[ActionPerformed] =
(blockchainRegistryActor ? CreateBlockchain).mapTo[ActionPerformed]
onSuccess(blockchainCreated) { performed =>
logBlockchainRoutes.info("Created Blockchain: {}", performed.description)
log.info("Created Blockchain: {}", performed.description)
complete((StatusCodes.Created, performed))
}
}
Expand All @@ -42,7 +40,7 @@ trait BlockchainRoutes extends JsonSupport with Routes {
val blockchainDeleted: Future[ActionPerformed] =
(blockchainRegistryActor ? DeleteBlockchain).mapTo[ActionPerformed]
onSuccess(blockchainDeleted) { performed =>
logBlockchainRoutes.info("Deleted Blockchain: {}", performed.description)
log.info("Deleted Blockchain: {}", performed.description)
complete((StatusCodes.OK, performed))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package com.fluency03.blockchain.api.routes
import akka.actor.ActorSystem
import akka.event.Logging
import akka.util.Timeout
import com.fluency03.blockchain.api.JsonSupport

import scala.concurrent.duration._

trait Routes {
trait Routes extends JsonSupport {
// we leave these abstract, since they will be provided by the App
implicit def system: ActorSystem

// Required by the `ask` (?) method below
implicit lazy val timeout: Timeout = Timeout(5.seconds) // usually we'd obtain the timeout from the system's configuration


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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.pattern.ask
import com.fluency03.blockchain.api.actors.BlockRegistryActor._
import com.fluency03.blockchain.core.Block

import scala.concurrent.Future

trait TransactionRoutes extends Routes {
lazy val log = Logging(system, classOf[TransactionRoutes])

def txRoutesRegistryActor: ActorRef

// lazy val transactionRoutes: Route =
// pathPrefix("transactions") {
// pathEnd {
// get {
// val blocks: Future[List[Block]] = (txRoutesRegistryActor ? GetBlocks).mapTo[List[Block]]
// complete(blocks)
// } ~
// post {
// entity(as[Block]) { block =>
// val blockCreated: Future[ActionPerformed] = (txRoutesRegistryActor ? CreateBlock(block)).mapTo[ActionPerformed]
// onSuccess(blockCreated) { performed =>
// log.info("Created user [{}]: {}", block.hash, performed.description)
// complete((StatusCodes.Created, performed))
// }
// }
// }
// } ~
// path(Segment) { hash =>
// get {
// val maybeBlock: Future[Option[Block]] = (txRoutesRegistryActor ? GetBlock(hash)).mapTo[Option[Block]]
// rejectEmptyResponse { complete(maybeBlock) }
// } ~
// delete {
// val blockDeleted: Future[ActionPerformed] = (txRoutesRegistryActor ? DeleteBlock(hash)).mapTo[ActionPerformed]
// onSuccess(blockDeleted) { performed =>
// log.info("Deleted block [{}]: {}", hash, performed.description)
// complete((StatusCodes.OK, performed))
// }
// }
// }
// }
}
7 changes: 5 additions & 2 deletions src/main/scala/com/fluency03/blockchain/core/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ case class Block(header: BlockHeader, transactions: List[Transaction] = List())
def addTransaction(sender: String, receiver: String, amount: Double): Block =
addTransaction(Transaction(sender, receiver, amount))

def addTransaction(sender: String, receiver: String, amount: Double, timestamp: Long): Block =
addTransaction(Transaction(sender, receiver, amount, timestamp))

def addTransactions(trans: List[Transaction]): Block =
Block(index, previousHash, data, trans ++ transactions, timestamp, nonce)

Expand Down Expand Up @@ -65,8 +68,8 @@ object Block {
0,
ZERO64,
"Welcome to Blockchain in Scala!",
List(Transaction(ZERO64, ZERO64, 50)),
Instant.parse("2018-04-11T18:52:01Z").getEpochSecond,
List(Transaction(ZERO64, ZERO64, 50, genesisTimestamp)),
genesisTimestamp,
difficulty)

def mineNextBlock(
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/com/fluency03/blockchain/core/Blockchain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ case class Blockchain(difficulty: Int = 4, chain: List[Block] = List(Block.genes
def addTransaction(sender: String, receiver: String, amount: Double): Blockchain =
addTransaction(Transaction(sender, receiver, amount))

def addTransaction(sender: String, receiver: String, amount: Double, timestamp: Long): Blockchain =
addTransaction(Transaction(sender, receiver, amount, timestamp))

def addTransactions(trans: List[Transaction]): Blockchain = {
currentTransactions ++= trans
this
Expand Down
14 changes: 9 additions & 5 deletions src/main/scala/com/fluency03/blockchain/core/Transaction.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.fluency03.blockchain.core

import com.fluency03.blockchain.Util.hashOf
import com.fluency03.blockchain.Util.{hashOf, getCurrentTimestamp}
import com.fluency03.blockchain.core.Transaction.hashOfTransaction
import org.json4s.native.JsonMethods.{compact, render}
import org.json4s.{Extraction, JValue}
Expand All @@ -10,8 +10,9 @@ import org.json4s.{Extraction, JValue}
* @param sender Sender of the current Transaction
* @param receiver Receiver of the current Transaction
* @param amount Amount of the current Transaction
* @param timestamp Unix epoch time of the current Transaction
*/
case class Transaction(sender: String, receiver: String, amount: Double) {
case class Transaction(sender: String, receiver: String, amount: Double, timestamp: Long) {
lazy val hash: String = hashOfTransaction(this)

def toJson: JValue = Extraction.decompose(this)
Expand All @@ -21,10 +22,13 @@ case class Transaction(sender: String, receiver: String, amount: Double) {

object Transaction {

def apply(sender: String, receiver: String, amount: Double): Transaction =
Transaction(sender, receiver, amount, getCurrentTimestamp)

def hashOfTransaction(tx: Transaction): String =
hashOfTransactionFields(tx.sender, tx.receiver, tx.amount)
hashOfTransactionFields(tx.sender, tx.receiver, tx.amount, tx.timestamp)

def hashOfTransactionFields(sender: String, receiver: String, amount: Double): String =
hashOf(sender, receiver, amount.toString)
def hashOfTransactionFields(sender: String, receiver: String, amount: Double, timestamp: Long): String =
hashOf(sender, receiver, amount.toString, timestamp.toString)

}
3 changes: 3 additions & 0 deletions src/main/scala/com/fluency03/blockchain/core/package.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.fluency03.blockchain

import java.time.Instant

import org.json4s.NoTypeHints
import org.json4s.native.Serialization

package object core {

implicit val formats = Serialization.formats(NoTypeHints)
lazy val ZERO64: String = "0000000000000000000000000000000000000000000000000000000000000000"
lazy val genesisTimestamp: Long = Instant.parse("2018-04-11T18:52:01Z").getEpochSecond

}
19 changes: 1 addition & 18 deletions src/test/resources/genesis-block.json
Original file line number Diff line number Diff line change
@@ -1,18 +1 @@
{
"header": {
"index": 0,
"previousHash": "0000000000000000000000000000000000000000000000000000000000000000",
"data": "Welcome to Blockchain in Scala!",
"merkleHash": "7814a9c43e9015462e5ffec1a3a9a69be024c1aacfa3ec4c879b5cd544761e7e",
"timestamp": 1523472721,
"nonce": 13860,
"hash": "00003607219f7a455e216f19ac3a34e3b158cf7282f7fdc624c93d593c2fc61f"
},
"transactions": [
{
"sender": "0000000000000000000000000000000000000000000000000000000000000000",
"receiver": "0000000000000000000000000000000000000000000000000000000000000000",
"amount": 50.0
}
]
}
{"header":{"index":0,"previousHash":"0000000000000000000000000000000000000000000000000000000000000000","data":"Welcome to Blockchain in Scala!","merkleHash":"21d1db6630265eb9e991b410d82870a8d3f62cb11d1d0917e926a49bdb3993b5","timestamp":1523472721,"nonce":33660,"hash":"0000a26af9a70022a6c6d270a0ced7478eb40bcfc4301b5e73c0ed3207a3de0e"},"transactions":[{"sender":"0000000000000000000000000000000000000000000000000000000000000000","receiver":"0000000000000000000000000000000000000000000000000000000000000000","amount":50.0,"timestamp":1523472721}]}
Loading

0 comments on commit 4857d6c

Please sign in to comment.