Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed Apr 27, 2018
2 parents aa36d2b + 048ab54 commit ee5d1de
Show file tree
Hide file tree
Showing 30 changed files with 317 additions and 199 deletions.
2 changes: 1 addition & 1 deletion src/main/resources/genesis-block.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"header":{"index":0,"previousHash":"0000000000000000000000000000000000000000000000000000000000000000","data":"Welcome to Blockchain in Scala!","merkleHash":"e580ae290899b8acc333fdb4e5ce52b6161c0df8f433a96cfcacbc4c211f1dc6","timestamp":1523472721,"difficulty":4,"nonce":289612},"transactions":[{"txIns":[{"previousOut":{"id":"","index":0},"signature":""}],"txOuts":[{"address":"3056301006072a8648ce3d020106052b8104000a034200049671ad288b396bdadf9d2d85640c6c61e14fa4a837b7b335bba21f226ba1525974c1a3f70fa1bc5a55c48ceced51468fe29bbbf67b22afa40383f99b98b841f9","amount":50}],"timestamp":1523472721,"id":"e580ae290899b8acc333fdb4e5ce52b6161c0df8f433a96cfcacbc4c211f1dc6"}],"hash":"0000d691591d3f999dce60c54719bc38b5bc2fb3ac39f76d9343b5ad4c01548b"}
{"header":{"index":0,"previousHash":"0000000000000000000000000000000000000000000000000000000000000000","data":"Welcome to Blockchain in Scala!","merkleHash":"47f123f38ab09c9abd1aa182e27ebf6a1a915a01ce7519fb68ac354978ea0350","timestamp":1523472721,"difficulty":4,"nonce":50466},"transactions":[{"txIns":[{"previousOut":{"id":"","index":0},"signature":""}],"txOuts":[{"address":"04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a2541129368d7d9bb05cd8afbdf7705a6540d98028236965553f91bf1c5b4f70073f55b55d","amount":50}],"timestamp":1523472721,"id":"47f123f38ab09c9abd1aa182e27ebf6a1a915a01ce7519fb68ac354978ea0350"}],"hash":"000005827dccd635761469d30df8b6e61dc4223630a4e2d2010e89ab4763e9de"}
2 changes: 1 addition & 1 deletion src/main/resources/private-key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
30818d020100301006072a8648ce3d020106052b8104000a04763074020101042005ab8a244673c5f9d86c64c28a0440368896a35daac9ec08a909de920c3dbc3aa00706052b8104000aa144034200049671ad288b396bdadf9d2d85640c6c61e14fa4a837b7b335bba21f226ba1525974c1a3f70fa1bc5a55c48ceced51468fe29bbbf67b22afa40383f99b98b841f9
36466d36ee0642a530b774de73c778a807a3b6b586201a1fabb0597c640836af
2 changes: 1 addition & 1 deletion src/main/resources/public-key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3056301006072a8648ce3d020106052b8104000a034200049671ad288b396bdadf9d2d85640c6c61e14fa4a837b7b335bba21f226ba1525974c1a3f70fa1bc5a55c48ceced51468fe29bbbf67b22afa40383f99b98b841f9
04b4d653fcbb4b96000c99343f23b08a44fa306031e0587f9e657ab4a2541129368d7d9bb05cd8afbdf7705a6540d98028236965553f91bf1c5b4f70073f55b55d
1 change: 0 additions & 1 deletion src/main/resources/signature

This file was deleted.

22 changes: 21 additions & 1 deletion src/main/scala/com/fluency03/blockchain/Crypto.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.fluency03.blockchain

import java.math.BigInteger
import java.security._
import java.security.spec.{PKCS8EncodedKeySpec, X509EncodedKeySpec}

import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.{ECPrivateKey, ECPublicKey}
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECParameterSpec
import org.bouncycastle.jce.spec.{ECParameterSpec, ECPrivateKeySpec, ECPublicKeySpec}

object Crypto {

Expand Down Expand Up @@ -45,4 +47,22 @@ object Crypto {
gen.generateKeyPair()
}

def generatePublicKey(privateKey: PrivateKey): PublicKey =
KeyFactory.getInstance(KEY_ALGORITHM)
.generatePublic(new ECPublicKeySpec(ecSpec.getG.multiply(privateKey.asInstanceOf[ECPrivateKey].getD), ecSpec))

def recoverPublicKey(hex: String): PublicKey =
KeyFactory.getInstance(KEY_ALGORITHM)
.generatePublic(new ECPublicKeySpec(ecSpec.getCurve.decodePoint(hex.hex2Bytes), ecSpec))

def recoverPrivateKey(hex: String): PrivateKey =
KeyFactory.getInstance(KEY_ALGORITHM)
.generatePrivate(new ECPrivateKeySpec(new BigInteger(hex, 16), ecSpec))

def publicKeyToHex(publicKey: PublicKey): String =
publicKey.asInstanceOf[ECPublicKey].getQ.getEncoded(false).toHex

def privateKeyToHex(privateKey: PrivateKey): String =
privateKey.asInstanceOf[ECPrivateKey].getD.toString(16)

}
15 changes: 10 additions & 5 deletions src/main/scala/com/fluency03/blockchain/api/actors/PeerActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import scala.collection.mutable

object PeerActor {
final case object GetPublicKeys
final case object CreateKeyPair
def props: Props = Props[PeerActor]
}

Expand All @@ -20,16 +21,20 @@ class PeerActor extends ActorSupport {

// TODO (Chang): need persistence
val wallets = mutable.Map.empty[String, KeyPair]
wallets += {
val pair: KeyPair = Crypto.generateKeyPair()
(pair.getPublic.getEncoded.toHex, pair)
}
addNewKeyPair()

val others = mutable.Map.empty[String, Peer]

def receive: Receive = {
case GetPublicKeys => sender() ! wallets.values.map(_.getPublic.getEncoded.toHex).toSet
case GetPublicKeys => sender() ! wallets.values.map(_.getPublic.toHex).toSet
case CreateKeyPair => sender() ! addNewKeyPair().getPublic.toHex
case _ => unhandled _
}

private def addNewKeyPair(): KeyPair = {
val pair: KeyPair = Crypto.generateKeyPair()
wallets += (pair.getPublic.toHex -> pair)
pair
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class TransactionsActor extends ActorSupport {

// TODO (Chang): need persistence
val currentTransactions: mutable.Map[String, Transaction] = mutable.Map.empty[String, Transaction]
val unspentTxOuts: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut]
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)
Expand Down
1 change: 0 additions & 1 deletion src/main/scala/com/fluency03/blockchain/api/package.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.fluency03.blockchain

import akka.http.scaladsl.model.{StatusCode, StatusCodes}
import com.fluency03.blockchain.core.{Block, Peer, Transaction}

package object api {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ trait BlockRoutes extends RoutesSupport {
post {
entity(as[Block]) { block =>
val blockCreated: Future[Message] = (blocksActor ? CreateBlock(block)).mapTo[Message]
onSuccess(blockCreated) {
case s: SuccessMsg => complete((StatusCodes.Created, s))
case f: FailureMsg => complete((StatusCodes.Conflict, f))
}
onSuccess(blockCreated) { respondOnCreation }
}
}
} ~
Expand All @@ -46,10 +43,7 @@ trait BlockRoutes extends RoutesSupport {
} ~
delete {
val blockDeleted: Future[Message] = (blocksActor ? DeleteBlock(hash)).mapTo[Message]
onSuccess(blockDeleted) {
case s: SuccessMsg => complete((StatusCodes.OK, s))
case f: FailureMsg => complete((StatusCodes.NotFound, f))
}
onSuccess(blockDeleted) { respondOnDeletion }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +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.RouteDirectives.complete
import akka.pattern.ask
import com.fluency03.blockchain.api.{FailureMsg, Message, SuccessMsg}
import com.fluency03.blockchain.api.Message
import com.fluency03.blockchain.api.actors.BlockchainActor._
import com.fluency03.blockchain.core.Blockchain
import org.json4s.JsonAST.JValue

import scala.concurrent.Future

Expand All @@ -28,20 +26,12 @@ trait BlockchainRoutes extends RoutesSupport {
rejectEmptyResponse { complete(blockchain) }
} ~
post {
entity(as[JValue]) { _ =>
val blockchainCreated: Future[Message] = (blockchainActor ? CreateBlockchain).mapTo[Message]
onSuccess(blockchainCreated) {
case s: SuccessMsg => complete((StatusCodes.Created, s))
case f: FailureMsg => complete((StatusCodes.Conflict, f))
}
}
val blockchainCreated: Future[Message] = (blockchainActor ? CreateBlockchain).mapTo[Message]
onSuccess(blockchainCreated) { respondOnCreation }
} ~
delete {
val blockchainDeleted: Future[Message] = (blockchainActor ? DeleteBlockchain).mapTo[Message]
onSuccess(blockchainDeleted) {
case s: SuccessMsg => complete((StatusCodes.OK, s))
case f: FailureMsg => complete((StatusCodes.NotFound, f))
}
onSuccess(blockchainDeleted) { respondOnDeletion }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ trait NetworkRoutes extends RoutesSupport {
post {
entity(as[PeerSimple]) { peer =>
val peerCreated: Future[Message] = (networkActor ? CreatePeer(peer.name)).mapTo[Message]
onSuccess(peerCreated) {
case s: SuccessMsg => complete((StatusCodes.Created, s))
case f: FailureMsg => complete((StatusCodes.Conflict, f))
}
onSuccess(peerCreated) { respondOnCreation }
}
}
} ~
Expand All @@ -46,10 +43,7 @@ trait NetworkRoutes extends RoutesSupport {
} ~
delete {
val peerDeleted: Future[Message] = (networkActor ? DeletePeer(name)).mapTo[Message]
onSuccess(peerDeleted) {
case s: SuccessMsg => complete((StatusCodes.OK, s))
case f: FailureMsg => complete((StatusCodes.NotFound, f))
}
onSuccess(peerDeleted) { respondOnDeletion }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.fluency03.blockchain.api.routes

import akka.actor.ActorSystem
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.{RequestContext, StandardRoute}
import akka.http.scaladsl.server.directives.RouteDirectives.complete
import akka.util.Timeout
import com.fluency03.blockchain.api.JsonSupport
import com.fluency03.blockchain.api.{FailureMsg, JsonSupport, Message, SuccessMsg}

import scala.concurrent.Future
import scala.concurrent.duration._

trait RoutesSupport extends JsonSupport {
Expand All @@ -13,4 +17,14 @@ trait RoutesSupport extends JsonSupport {
// Required by the `ask` (?) method
implicit lazy val timeout: Timeout = Timeout(5.seconds) // usually we'd obtain the timeout from the system's configuration

def respondOnCreation(m: Message): StandardRoute = m match {
case s: SuccessMsg => complete((StatusCodes.Created, s))
case f: FailureMsg => complete((StatusCodes.Conflict, f))
}

def respondOnDeletion(m: Message): StandardRoute = m match {
case s: SuccessMsg => complete((StatusCodes.OK, s))
case f: FailureMsg => complete((StatusCodes.NotFound, f))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ trait TransactionRoutes extends RoutesSupport {
post {
entity(as[Transaction]) { tx =>
val txCreated: Future[Message] = (transActor ? CreateTransaction(tx)).mapTo[Message]
onSuccess(txCreated) {
case s: SuccessMsg => complete((StatusCodes.Created, s))
case f: FailureMsg => complete((StatusCodes.Conflict, f))
}
onSuccess(txCreated) { respondOnCreation }
}
}
} ~
Expand All @@ -46,10 +43,7 @@ trait TransactionRoutes extends RoutesSupport {
} ~
delete {
val txDeleted: Future[Message] = (transActor ? DeleteTransaction(id)).mapTo[Message]
onSuccess(txDeleted) {
case s: SuccessMsg => complete((StatusCodes.OK, s))
case f: FailureMsg => complete((StatusCodes.NotFound, f))
}
onSuccess(txDeleted) { respondOnDeletion }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,4 @@ package object routes {
}






}
38 changes: 20 additions & 18 deletions src/main/scala/com/fluency03/blockchain/core/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package core

import com.fluency03.blockchain.core.BlockHeader.hashOfHeaderFields
import com.fluency03.blockchain.core.Transaction.createCoinbaseTx
import org.json4s.JsonAST.JObject
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods.{compact, render}
import org.json4s.{Extraction, JValue}

Expand All @@ -13,16 +11,14 @@ import org.json4s.{Extraction, JValue}
* @param header Header of current Block
* @param transactions Seq of Transactions included in current Block
*/
case class Block(header: BlockHeader, transactions: Seq[Transaction] = Seq()) {
lazy val index: Int = header.index
lazy val previousHash: String = header.previousHash
lazy val data: String = header.data
lazy val merkleHash: String = header.merkleHash
lazy val timestamp: Long = header.timestamp
lazy val difficulty: Int = header.difficulty
lazy val nonce: Int = header.nonce

lazy val hash: String = header.hash
case class Block(header: BlockHeader, transactions: Seq[Transaction], hash: String) {
def index: Int = header.index
def previousHash: String = header.previousHash
def data: String = header.data
def merkleHash: String = header.merkleHash
def timestamp: Long = header.timestamp
def difficulty: Int = header.difficulty
def nonce: Int = header.nonce

def nextTrial(): Block = Block(header.nextTrial(), transactions)

Expand All @@ -35,17 +31,22 @@ case class Block(header: BlockHeader, transactions: Seq[Transaction] = Seq()) {
def removeTransaction(tx: Transaction): Block =
Block(index, previousHash, data, timestamp, difficulty, nonce, transactions.filter(_ != tx))

def isValid: Boolean = isWithValidDifficulty(hash, difficulty) && hasValidMerkleHash
def hasValidHash: Boolean = hasValidHeaderHash && isWithValidDifficulty(hash, difficulty) && hasValidMerkleHash

def hasValidMerkleHash: Boolean = merkleHash == Merkle.computeRoot(transactions)

def toJson: JValue = ("header" -> header.toJson) ~ ("transactions" -> transactions.map(_.toJson)) ~ ("hash" -> hash)
def hasValidHeaderHash: Boolean = hash == header.hash

def toJson: JValue = Extraction.decompose(this)

override def toString: String = compact(render(toJson))
}

object Block {

def apply(header: BlockHeader, transactions: Seq[Transaction] = Seq()): Block =
Block(header, transactions, header.hash)

def apply(
index: Int,
previousHash: String,
Expand Down Expand Up @@ -80,9 +81,9 @@ object Block {

lazy val genesisBlock: Block = genesis()

def genesis(difficulty: Int = 4): Block =
mineNextBlock(0, ZERO64, "Welcome to Blockchain in Scala!", genesisTimestamp, difficulty,
Seq(createCoinbaseTx(0, genesisMiner, genesisTimestamp)))
def genesis(difficulty: Int = 4): Block = mineNextBlock(
0, ZERO64, "Welcome to Blockchain in Scala!", genesisTimestamp, difficulty,
Seq(createCoinbaseTx(0, genesisMiner, genesisTimestamp)))

def mineNextBlock(
nextIndex: Int,
Expand Down Expand Up @@ -111,8 +112,9 @@ object Block {
transactions: Seq[Transaction]): Block =
mineNextBlock(currentBlock.index + 1, currentBlock.hash, newBlockData, timestamp, difficulty, transactions)

// TODO (Chang): Check transactions in the new block?
def canBeChained(newBlock: Block, previousBlock: Block): Boolean =
previousBlock.index + 1 == newBlock.index && previousBlock.hash == newBlock.previousHash
previousBlock.index + 1 == newBlock.index && previousBlock.hash == newBlock.previousHash && newBlock.hasValidHash



Expand Down
14 changes: 7 additions & 7 deletions src/main/scala/com/fluency03/blockchain/core/BlockHeader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ object BlockHeader {

def hashOfBlockHeader(header: BlockHeader): String =
hashOfHeaderFields(
header.index,
header.previousHash,
header.data,
header.merkleHash,
header.timestamp,
header.difficulty,
header.nonce)
header.index,
header.previousHash,
header.data,
header.merkleHash,
header.timestamp,
header.difficulty,
header.nonce)

def hashOfHeaderFields(
index: Int,
Expand Down
Loading

0 comments on commit ee5d1de

Please sign in to comment.