Skip to content

Commit

Permalink
fix style in core and crypto
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed May 12, 2018
1 parent da560dc commit 71cbe9f
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 64 deletions.
16 changes: 11 additions & 5 deletions src/main/scala/com/github/fluency03/blockchain/api/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ import com.typesafe.config.ConfigFactory
import scala.concurrent.{Await, ExecutionContextExecutor, Future}
import scala.concurrent.duration.Duration

object Server extends App
with BlockchainRoutes with BlockPoolRoutes with TxPoolRoutes with NetworkRoutes with GenericRoutes {
object Server
extends App
with BlockchainRoutes
with BlockPoolRoutes
with TxPoolRoutes
with NetworkRoutes
with GenericRoutes {

// we leave these abstract, since they will be provided by the App
implicit val system: ActorSystem = ActorSystem("blockchain-http-service")
implicit val materializer: ActorMaterializer = ActorMaterializer()
Expand All @@ -32,10 +38,10 @@ object Server extends App
val txPoolActor: ActorRef = system.actorOf(TxPoolActor.props, TX_POOL_ACTOR_NAME)
val networkActor: ActorRef = system.actorOf(NetworkActor.props, NETWORK_ACTOR_NAME)

lazy val routes: Route = blockchainRoutes ~ blockPoolRoutes ~ txPoolRoutes ~ networkRoutes ~ genericRoutes
lazy val routes: Route =
blockchainRoutes ~ blockPoolRoutes ~ txPoolRoutes ~ networkRoutes ~ genericRoutes

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

bindingFuture.failed.foreach { ex =>
log.error(ex, "Failed to bind to {}:{}!", host, port)
Expand Down
62 changes: 40 additions & 22 deletions src/main/scala/com/github/fluency03/blockchain/core/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package core

import com.github.fluency03.blockchain.core.Block.allTransValidOf
import com.github.fluency03.blockchain.core.BlockHeader.hashOfHeaderFields
import com.github.fluency03.blockchain.core.Transaction.{createCoinbaseTx, validateCoinbaseTx, noDuplicateTxInOf}
import com.github.fluency03.blockchain.core.Transaction.{
createCoinbaseTx, noDuplicateTxInOf, validateCoinbaseTx}
import org.json4s.native.JsonMethods.{compact, render}
import org.json4s.{Extraction, JValue}

Expand Down Expand Up @@ -34,13 +35,15 @@ case class Block(header: BlockHeader, transactions: Seq[Transaction], hash: Stri
def removeTransaction(tx: Transaction): Block =
Block(index, previousHash, data, timestamp, difficulty, nonce, transactions.filter(_ != tx))

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

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

def hasValidHeaderHash: Boolean = hash == header.hash

def allTransAreValid(uTxOs: mutable.Map[Outpoint, TxOut]): Boolean = allTransValidOf(transactions, index, uTxOs)
def allTransAreValid(uTxOs: mutable.Map[Outpoint, TxOut]): Boolean =
allTransValidOf(transactions, index, uTxOs)

def noDuplicateTxIn(): Boolean = noDuplicateTxInOf(transactions)

Expand Down Expand Up @@ -71,9 +74,12 @@ object Block {
timestamp: Long,
difficulty: Int,
nonce: Int,
transactions: Seq[Transaction]): Block =
Block(BlockHeader(index, previousHash, data, Merkle.computeRoot(transactions), timestamp, difficulty, nonce),
transactions)
transactions: Seq[Transaction]): Block = {
val merkleRoot = Merkle.computeRoot(transactions)
val header = BlockHeader(index, previousHash, data, merkleRoot, timestamp, difficulty, nonce)
Block(header, transactions)
}


def apply(
index: Int,
Expand All @@ -83,13 +89,19 @@ object Block {
timestamp: Long,
difficulty: Int,
nonce: Int,
transactions: Seq[Transaction]): Block =
Block(BlockHeader(index, previousHash, data, merkleHash, timestamp, difficulty, nonce), transactions)
transactions: Seq[Transaction]): Block = {
val header = BlockHeader(index, previousHash, data, merkleHash, timestamp, difficulty, nonce)
Block(header, transactions)
}

lazy val genesisBlock: Block = genesis()

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

// TODO (Chang): implement parallel version
Expand All @@ -104,9 +116,12 @@ object Block {
var nextHash = ""
val merkleHash = Merkle.computeRoot(transactions)

val hashOnNonce: Int => String =
hashOfHeaderFields(nextIndex, prevHash, newBlockData, merkleHash, timestamp, difficulty, _)

while (!isWithValidDifficulty(nextHash, difficulty)) {
nonce += 1
nextHash = hashOfHeaderFields(nextIndex, prevHash, newBlockData, merkleHash, timestamp, difficulty, nonce)
nextHash = hashOnNonce(nonce)
}

Block(nextIndex, prevHash, newBlockData, merkleHash, timestamp, difficulty, nonce, transactions)
Expand All @@ -118,35 +133,38 @@ object Block {
timestamp: Long,
difficulty: Int,
transactions: Seq[Transaction]): Block =
mineNextBlock(currentBlock.index + 1, currentBlock.hash, newBlockData, timestamp, difficulty, transactions)
mineNextBlock(currentBlock.index + 1, currentBlock.hash, newBlockData, timestamp, difficulty,
transactions)

def mineNextBlock(
currentBlock: Block,
newBlockData: String,
timestamp: Long,
difficulty: Int,
transactions: Seq[Transaction],
miner: String): Block =
currentBlock: Block,
newBlockData: String,
timestamp: Long,
difficulty: Int,
transactions: Seq[Transaction],
miner: String): Block =
mineNextBlock(currentBlock.index + 1, currentBlock.hash, newBlockData, timestamp, difficulty,
transactions :+ createCoinbaseTx(currentBlock.index + 1, miner, timestamp))

/**
* Check whether transactions of a Block are valid:
* 1. Coinbase transaction is valid
* 2. Rest of the transactions are valid
* 3. if the Seq is empty, then it is not valid, because it has to at least contain one coinbase transaction
* 3. if the Seq is empty, then it is not valid, because it has to at least contain one
* coinbase transaction
*/
def allTransValidOf(
transactions: Seq[Transaction],
blockIndex: Int,
uTxOs: mutable.Map[Outpoint, TxOut])
: Boolean = transactions match {
uTxOs: mutable.Map[Outpoint, TxOut]): Boolean = transactions match {
case Nil => false
case init :+ last => validateCoinbaseTx(last, blockIndex) && init.forall(tx => tx.isValid(uTxOs))
case init :+ last =>
validateCoinbaseTx(last, blockIndex) && init.forall(tx => tx.isValid(uTxOs))
}

/**
* Check whether a new Block can be chained to the last Block of a Blockchain (previousBlock of newBlock):
* Check whether a new Block can be chained to the last Block of a Blockchain
* (previousBlock of newBlock):
* 1. New Block's index should be the previous index plus one
* 2. New Block's previousHash should be the hash of previous Block
* 3. New Block should have valid hash (that means, valid header hash and merkle hash)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ case class BlockHeader(

lazy val hash: String = hashOfBlockHeader(this)

def nextTrial(): BlockHeader = BlockHeader(index, previousHash, data, merkleHash, timestamp, difficulty, nonce + 1)
def nextTrial(): BlockHeader =
BlockHeader(index, previousHash, data, merkleHash, timestamp, difficulty, nonce + 1)

def toJson: JValue = Extraction.decompose(this)

Expand Down Expand Up @@ -56,6 +57,13 @@ object BlockHeader {
timestamp: Long,
difficulty: Int,
nonce: Int): String =
SHA256.hashAll(index.toString, previousHash, data, merkleHash, timestamp.toString, difficulty.toString, nonce.toString)
SHA256.hashAll(
index.toString,
previousHash,
data,
merkleHash,
timestamp.toString,
difficulty.toString,
nonce.toString)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.github.fluency03.blockchain
package core

import com.github.fluency03.blockchain.core.Block.validLinkBetween
import com.github.fluency03.blockchain.core.Blockchain._
import com.github.fluency03.blockchain.core.Blockchain.isValidChain
import org.json4s.native.JsonMethods.{compact, render}
import org.json4s.{Extraction, JValue}

Expand All @@ -25,13 +25,13 @@ case class Blockchain(difficulty: Int = 4, chain: Seq[Block] = Seq(Block.genesis

def lastBlock(): Option[Block] = chain.headOption

def mineNextBlock(newBlockData: String, transactions: Seq[Transaction]): Block = {
val lastBlockOpt: Option[Block] = this.lastBlock()
if (lastBlockOpt.isEmpty) throw new NoSuchElementException("Last Block does not exist!")
val lastHeader = lastBlockOpt.get.header
Block.mineNextBlock(lastHeader.index + 1, lastHeader.hash, newBlockData, getCurrentTimestamp, difficulty,
transactions)
}
def mineNextBlock(newBlockData: String, transactions: Seq[Transaction]): Block =
this.lastBlock() match {
case Some(block) =>
Block.mineNextBlock(
block.index + 1, block.hash, newBlockData, getCurrentTimestamp, difficulty, transactions)
case None => throw new NoSuchElementException("Last Block does not exist!")
}

def isValid: Boolean = chain match {
case Nil => throw new NoSuchElementException("Blockchain is Empty!")
Expand All @@ -48,7 +48,8 @@ case class Blockchain(difficulty: Int = 4, chain: Seq[Block] = Seq(Block.genesis

object Blockchain {

def apply(difficulty: Int): Blockchain = new Blockchain(difficulty, Seq(Block.genesis(difficulty)))
def apply(difficulty: Int): Blockchain =
new Blockchain(difficulty, Seq(Block.genesis(difficulty)))

// TODO (Chang): return both the validity and highest valid Block index
@tailrec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ object Merkle {
def hashViaMerklePath(init: String, path: Seq[String], index: Int): String =
if (path.isEmpty) init
else {
val newHash = if (index % 2 == 0) SHA256.hashAll(init, path.head) else SHA256.hashAll(path.head, init)
val newHash = if (index % 2 == 0)
SHA256.hashAll(init, path.head)
else
SHA256.hashAll(path.head, init)
hashViaMerklePath(newHash, path.tail, index/2)
}

def simplifiedPaymentVerification(init: String, root: String, path: Seq[String], index: Int): Boolean =
def verifySimplified(init: String, root: String, path: Seq[String], index: Int): Boolean =
hashViaMerklePath(init, path, index) == root


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ case class Transaction(txIns: Seq[TxIn], txOuts: Seq[TxOut], timestamp: Long, id

def removeTxIn(txIn: TxIn): Transaction = Transaction(txIns.filter(_ != txIn), txOuts, timestamp)

def removeTxOut(txOut: TxOut): Transaction = Transaction(txIns, txOuts.filter(_ != txOut), timestamp)
def removeTxOut(txOut: TxOut): Transaction =
Transaction(txIns, txOuts.filter(_ != txOut), timestamp)

def hasValidId: Boolean = id == hashOfTransaction(this)

Expand Down Expand Up @@ -69,20 +70,28 @@ object Transaction {
tx.txOuts.map(tx => tx.address + tx.amount).mkString,
tx.timestamp.toString)

def hashOfTransaction(txIns: Seq[TxIn], txOuts: Seq[TxOut], timestamp: Long): String = SHA256.hashAll(
txIns.map(tx => tx.previousOut.id + tx.previousOut.index).mkString,
txOuts.map(tx => tx.address + tx.amount).mkString,
timestamp.toString)
def hashOfTransaction(txIns: Seq[TxIn], txOuts: Seq[TxOut], timestamp: Long): String =
SHA256.hashAll(
txIns.map(tx => tx.previousOut.id + tx.previousOut.index).mkString,
txOuts.map(tx => tx.address + tx.amount).mkString,
timestamp.toString)

// sign TxIn
def signTxIn(txId: String, txIn: TxIn, keyPair: KeyPair, uTxOs: mutable.Map[Outpoint, TxOut]): Option[TxIn] =
def signTxIn(
txId: String,
txIn: TxIn,
keyPair: KeyPair,
uTxOs: mutable.Map[Outpoint, TxOut]): Option[TxIn] =
signTxIn(txId.hex2Bytes, txIn, keyPair, uTxOs)

def signTxIn(txId: Bytes, txIn: TxIn, keyPair: KeyPair, uTxOs: mutable.Map[Outpoint, TxOut]): Option[TxIn] =
uTxOs.get(txIn.previousOut) match {
case Some(uTxO) => signTxIn(txId, txIn, keyPair, uTxO)
case None => None
}
def signTxIn(
txId: Bytes,
txIn: TxIn,
keyPair: KeyPair,
uTxOs: mutable.Map[Outpoint, TxOut]): Option[TxIn] = uTxOs.get(txIn.previousOut) match {
case Some(uTxO) => signTxIn(txId, txIn, keyPair, uTxO)
case None => None
}

def signTxIn(txId: Bytes, txIn: TxIn, keyPair: KeyPair, uTxO: TxOut): Option[TxIn] =
if (keyPair.getPublic.toHex != uTxO.address) None
Expand All @@ -105,7 +114,10 @@ object Transaction {
def validateTxOutValues(transaction: Transaction, uTxOs: mutable.Map[Outpoint, TxOut]): Boolean =
validateTxOutValues(transaction.txIns, transaction.txOuts, uTxOs)

def validateTxOutValues(txIns: Seq[TxIn], txOuts: Seq[TxOut], uTxOs: mutable.Map[Outpoint, TxOut]): Boolean =
def validateTxOutValues(
txIns: Seq[TxIn],
txOuts: Seq[TxOut],
uTxOs: mutable.Map[Outpoint, TxOut]): Boolean =
txIns.map(txIn => uTxOs.get(txIn.previousOut) match {
case Some(txOut) => txOut.amount
case None => 0
Expand All @@ -125,7 +137,9 @@ object Transaction {
* 1. Remove all consumed unspent transaction outputs
* 2. Append all new unspent transaction outputs
*/
def updateUTxOs(transactions: Seq[Transaction], uTxOs: Map[Outpoint, TxOut]): Map[Outpoint, TxOut] = {
def updateUTxOs(
transactions: Seq[Transaction],
uTxOs: Map[Outpoint, TxOut]): Map[Outpoint, TxOut] = {
val consumedTxOuts = getConsumedUTxOs(transactions)
uTxOs.filterNot {
case (i, _) => consumedTxOuts.contains(i)
Expand All @@ -134,15 +148,15 @@ object Transaction {

def getNewUTxOs(transactions: Seq[Transaction]): Map[Outpoint, TxOut] =
transactions
.map(t => t.txOuts.zipWithIndex.map {
.map { t => t.txOuts.zipWithIndex.map {
case (txOut, index) => Outpoint(t.id, index) -> txOut
}.toMap)
}.toMap }
.foldLeft(Map.empty[Outpoint, TxOut])(_ ++ _)

def getConsumedUTxOs(transactions: Seq[Transaction]): Map[Outpoint, TxOut] =
transactions.map(_.txIns)
.foldLeft(Seq.empty[TxIn])(_ ++ _)
.map(txIn => Outpoint(txIn.previousOut.id, txIn.previousOut.index) -> TxOut("", 0))
.map { txIn => Outpoint(txIn.previousOut.id, txIn.previousOut.index) -> TxOut("", 0) }
.toMap

def noDuplicateTxInOf(transactions: Seq[Transaction]): Boolean = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ object Base58 {

def decodeToHex(str: String): String = new String(decode(str))

def checkEncode(bytes: Bytes): String = encode(bytes ++ bytes.toSha256Digest.toSha256Digest.slice(0, 4))
def checkEncode(bytes: Bytes): String =
encode(bytes ++ bytes.toSha256Digest.toSha256Digest.slice(0, 4))


}
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ object Secp256k1 {
}

def generatePublicKey(privateKey: PrivateKey): PublicKey =
KeyFactory.getInstance(KEY_ALGORITHM)
.generatePublic(new ECPublicKeySpec(ecSpec.getG.multiply(privateKey.asInstanceOf[ECPrivateKey].getD), ecSpec))
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))
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))
KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(
new ECPrivateKeySpec(new BigInteger(hex, 16), ecSpec))

def publicKeyToHex(publicKey: PublicKey): String = publicKeyToBytes(publicKey).toHex

Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/com/github/fluency03/blockchain/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ package object blockchain {
/**
* Check whether the given hash is with valid difficulty.
*/
def isWithValidDifficulty(hash: String, difficulty: Int): Boolean = hash startsWith ("0" * difficulty)
def isWithValidDifficulty(hash: String, difficulty: Int): Boolean =
hash.startsWith("0" * difficulty)

/**
* Encode a String to Base64.
Expand Down

0 comments on commit 71cbe9f

Please sign in to comment.