Skip to content

Commit

Permalink
add Difficulty
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed Apr 16, 2018
1 parent 43729c2 commit b52f6e1
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# blockchain-scala
# Blockchain in Scala

[![Build Status](https://travis-ci.org/fluency03/blockchain-in-scala.svg?branch=master)](https://travis-ci.org/fluency03/blockchain-in-scala)
[![Coverage Status](https://coveralls.io/repos/github/fluency03/blockchain-in-scala/badge.svg?branch=master)](https://coveralls.io/github/fluency03/blockchain-in-scala?branch=master)
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/com/fluency03/blockchain/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import java.util.Base64

object Util {



/**
* Generate SHA256 Hash from a input String.
* https://gist.github.com/navicore/6234040bbfce3aa58f866db314c07c15
Expand Down
65 changes: 65 additions & 0 deletions src/main/scala/com/fluency03/blockchain/core/Difficulty.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.fluency03.blockchain.core

object Difficulty {

lazy val difficultyOneTarget: BigInt = targetOfBits("1d00ffff".hex)

/**
* https://github.com/bitcoin/bitcoin/blob/master/src/arith_uint256.cpp#L206
*/
def decodeCompact(nCompact: Long): (BigInt, Boolean, Boolean) = {
val nSize = (nCompact >> 24).toInt
val (nWord, result) = if (nSize <= 3) {
val nWord1 = (nCompact & 0x007fffff) >> 8 * (3 - nSize)
(nWord1, BigInt(nWord1))
} else {
val nWord1 = nCompact & 0x007fffff
(nWord1, BigInt(nWord1) << (8 * (nSize - 3)))
}
val fNegative = nWord != 0 && (nCompact & 0x00800000) != 0
val fOverflow = nWord != 0 && ((nSize > 34) || (nWord > 0xff && nSize > 33) || (nWord > 0xffff && nSize > 32))
(result, fNegative, fOverflow)
}

// def encodeCompact(target: BigInt, fNegative: Boolean): Long = {
// val bitLength = target.bitLength
// var nSize = ((if (bitLength == 0) 0 else bitLength + 1) + 7) / 8
// var nCompact: Long = if (nSize <= 3) {
// getLowBits(target, 64) << 8 * (3 - nSize)
// } else {
// val bn = target >> 8 * (nSize - 3)
// getLowBits(bn, 64)
// }
// // The 0x00800000 bit denotes the sign.
// // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
// if ((nCompact & 0x00800000) != 0) (nCompact >>= 8, nSize += 1)
//
// assert((nCompact & ~0x007fffff) == 0)
// assert(nSize < 256)
// nCompact |= nSize << 24
// nCompact |= (if (fNegative && ((nCompact & 0x007fffff) != 0)) 0x00800000 else 0)
// nCompact
// }

def getLowBits(x: BigInt, N: Int): Long = (x & ((1 << N) - 1)).toLong

def targetOfBits(bitsInt: Long): BigInt = {
val (result, fNegative, _) = decodeCompact(bitsInt)
if (fNegative) -result else result
}

// def bitsOfTarget(target: BigInt, fNegative: Boolean): Long = encodeCompact(target, fNegative)

def padHexTarget(hex: String): String = hex.length match {
case len => "0" * (64 - len) + hex
}

def hashLessThanTarget(hash: String, target: String): Boolean = ???

def difficultyOf(target: BigInt, negative: Boolean, overflow: Boolean): Double = {
if (target == 0 || negative || overflow) 0.0
else (BigDecimal(difficultyOneTarget) / BigDecimal(target)).toDouble
}


}
9 changes: 8 additions & 1 deletion src/main/scala/com/fluency03/blockchain/core/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ 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

class HexString(val s: String) {
def hex: Long = java.lang.Long.parseLong(s, 16)
}

implicit def str2Hex(str: String): HexString = new HexString(str)

}
4 changes: 4 additions & 0 deletions src/test/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
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class BlockchainTest extends FlatSpec with Matchers {
blockchain.lastBlock().isEmpty shouldEqual false
blockchain.lastBlock().get shouldEqual genesis
blockchain.currentTransactions shouldEqual mutable.Map.empty[String, Transaction]
blockchain.isValid shouldEqual true
}

"A new Blockchain with different difficulty" should "have all default values but the difficulty." in {
Expand All @@ -32,6 +33,7 @@ class BlockchainTest extends FlatSpec with Matchers {
blockchainOf5.lastBlock().isEmpty shouldEqual false
blockchainOf5.lastBlock().get shouldEqual genesisOf5
blockchainOf5.currentTransactions shouldEqual mutable.Map.empty[String, Transaction]
blockchainOf5.isValid shouldEqual true
}

"Add a Transaction to a Blockchain" should "add these Transactions to its currentTransactions collection." in {
Expand All @@ -45,6 +47,7 @@ class BlockchainTest extends FlatSpec with Matchers {
trans += (t2.hash -> t2)
trans += (t3.hash -> t3)
blockchain.currentTransactions shouldEqual trans
blockchain.isValid shouldEqual true
}

"Add a List of Transaction to a Blockchain" should "add these Transactions to its currentTransactions collection." in {
Expand All @@ -54,6 +57,7 @@ class BlockchainTest extends FlatSpec with Matchers {
trans += (t3.hash -> t3)
trans += (t4.hash -> t4)
blockchainOf5.currentTransactions shouldEqual trans
blockchainOf5.isValid shouldEqual true
}

"Blockchain" should "be able to mine the next Block." in {
Expand All @@ -65,8 +69,10 @@ class BlockchainTest extends FlatSpec with Matchers {
actual shouldEqual expected

blockchainToAdd.lastBlock().get shouldEqual genesis
blockchainToAdd.isValid shouldEqual true
val blockchainAdded = blockchainToAdd.addBlock(actual)
blockchainAdded.lastBlock().get shouldEqual expected
blockchainAdded.isValid shouldEqual true
}


Expand Down
42 changes: 42 additions & 0 deletions src/test/scala/com/fluency03/blockchain/core/DifficultyTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.fluency03.blockchain.core

import com.fluency03.blockchain.core.Difficulty._
import org.scalatest.{FlatSpec, Matchers}

class DifficultyTest extends FlatSpec with Matchers {

val (t1, n1, o1) = decodeCompact("1d00ffff".hex)
val (t2, n2, o2) = decodeCompact("453062093".toLong)
val (t3, n3, o3) = decodeCompact("-453062093".toLong)

"Bits in different formats" should "have its corresponding targets." in {
difficultyOneTarget shouldEqual t1
t1.toString(16) shouldEqual "ffff0000000000000000000000000000000000000000000000000000"
t2.toString(16) shouldEqual "12dcd000000000000000000000000000000000000000000000000"
targetOfBits("01003456".hex) shouldEqual "00".hex
targetOfBits("01123456".hex) shouldEqual "12".hex
targetOfBits("02008000".hex) shouldEqual "80".hex
targetOfBits("05009234".hex) shouldEqual "92340000".hex
targetOfBits("04923456".hex) shouldEqual - "12345600".hex
targetOfBits("04123456".hex) shouldEqual "12345600".hex
// println("1d00ffff".hex)
// println(bitsOfTarget(t1, n1))
// println(n1, o1)
// println(bitsOfTarget(t2, n2))
// println(n2, o2)
// println(bitsOfTarget(t3, n3))
// println(n3, o3)
}

"padHexTarget" should "add enough zeros (0) in front of a hex fot making it 64 bytes." in {
padHexTarget(t1.toString(16)) shouldEqual "00000000ffff0000000000000000000000000000000000000000000000000000"
padHexTarget(t2.toString(16)) shouldEqual "0000000000012dcd000000000000000000000000000000000000000000000000"
difficultyOf(t1, n1, o1) shouldEqual 1.0
difficultyOf(t2, n2, o2) shouldEqual 55589.518126868665
// targetOfBits(bitsOfTarget(t1, n1)) shouldEqual t1
// targetOfBits(bitsOfTarget(t2, n2)) shouldEqual t2
}



}

0 comments on commit b52f6e1

Please sign in to comment.