Implement difficulty calculation from nBits (#377)

* Implement difficulty calculation from nBits

* Torkel's suggest to use bitvector indexing rather than bitmasks

* Explicitly import BitcoinSUnitTest
This commit is contained in:
Chris Stewart 2019-03-12 07:49:51 -05:00 committed by GitHub
parent eaa87b1e2b
commit 35493249cc
3 changed files with 133 additions and 1 deletions

View file

@ -0,0 +1,72 @@
package org.bitcoins.core.util
import java.math.BigInteger
import org.bitcoins.core.number.UInt32
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.scalatest.Assertion
import scodec.bits.ByteVector
class NumberUtilTest extends BitcoinSUnitTest {
behavior of "NumberUtil"
it must "expand nbits to 0 difficulty threshold" in {
//from the examples table on bitcoin developer reference site
//https://bitcoin.org/en/developer-reference#target-nbits
val nBits1 = UInt32.fromHex("01003456")
val expected1 = BigInteger.valueOf(0)
runTest(nBits1, expected1)
val nBits2 = UInt32.fromHex("01123456")
val expected2 = BigInteger.valueOf(18)
runTest(nBits2, expected2)
val nBits3 = UInt32.fromHex("02008000")
val expected3 = BigInteger.valueOf(128)
runTest(nBits3, expected3)
val nBits4 = UInt32.fromHex("05009234")
val expected4 = BigInteger.valueOf(2452881408L)
runTest(nBits4, expected4)
val nBits6 = UInt32.fromHex("04123456")
val expected6 = BigInteger.valueOf(305419776)
runTest(nBits6, expected6)
val nBits5 = UInt32.fromHex("04923456")
val expected5 = BigInteger.valueOf(-305419776)
runTest(nBits5, expected5)
}
it must "expand the minimum difficulty on bitcoin main network" in {
//https://stackoverflow.com/questions/22059359/trying-to-understand-nbits-value-from-stratum-protocol
val nBits = UInt32.fromHex("1d00ffff")
val expected = new BigInteger(
"00ffff0000000000000000000000000000000000000000000000000000",
16)
runTest(nBits, expected)
}
it must "expand the minimum difficulty correctly on bitcoin regtest" in {
val nBits = UInt32.fromHex("207fffff")
val expected = new BigInteger(
"57896037716911750921221705069588091649609539881711309849342236841432341020672",
10
)
runTest(nBits, expected)
}
private def runTest(nBits: UInt32, expected: BigInteger): Assertion = {
assert(NumberUtil.targetExpansion(nBits) == expected)
}
}

View file

@ -1,10 +1,12 @@
package org.bitcoins.core.protocol.blockchain
import java.math.BigInteger
import org.bitcoins.core.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.NetworkElement
import org.bitcoins.core.serializers.blockchain.RawBlockHeaderSerializer
import org.bitcoins.core.util.{CryptoUtil, Factory}
import org.bitcoins.core.util.{CryptoUtil, Factory, NumberUtil}
import scodec.bits.ByteVector
/**
@ -86,6 +88,20 @@ sealed trait BlockHeader extends NetworkElement {
*/
def nBits: UInt32
/**
* This is the decoded version of [[nBits]]. nBits is used to compactly represent the difficulty
* target for the bitcoin network. This field is the expanded version that is the _actual_
* requirement needed for the network. This is a 256 bit unsigned integer
* See the bitcoin developer reference for more information on how this is constructed
* [[https://bitcoin.org/en/developer-reference#target-nbits documentation]]
*
* The hash of this block needs to be _less than_ this difficulty
* to be considered a valid block on the network
*/
def difficulty: BigInteger = {
NumberUtil.targetExpansion(nBits = nBits)
}
/**
* An arbitrary number miners change to modify the header hash in order to produce a hash below the target threshold.
* If all 32-bit values are tested, the time can be updated or the coinbase

View file

@ -144,6 +144,50 @@ trait NumberUtil extends BitcoinSLogger {
//should always be able to convert from uint5 => uint8
u8sTry.get
}
/** Expands the [[org.bitcoins.core.protocol.blockchain.BlockHeader.nBits nBits]]
* field given in a block header to the _actual_ target difficulty.
* See [[https://bitcoin.org/en/developer-reference#target-nbits developer reference]]
* for more information
*
* Meant to replicate this function in bitcoin core
* [[https://github.com/bitcoin/bitcoin/blob/2068f089c8b7b90eb4557d3f67ea0f0ed2059a23/src/arith_uint256.cpp#L206]]
* @param nBits
* @return
*/
def targetExpansion(nBits: UInt32): BigInteger = {
//mantissa bytes without sign bit
val noSignificand = nBits.bytes.takeRight(3)
val mantissaBytes = {
val withSignBit = noSignificand
val noSignBit = false +: withSignBit.bits.tail
noSignBit.toByteVector
}
val significand = nBits.bytes.head
//if the most significant bit is set, we have a negative number
val signum = if (noSignificand.bits.head) {
-1
} else {
1
}
val mantissa =
new BigInteger(signum, mantissaBytes.toArray)
//guards against a negative exponent, in which case we just shift right
//see bitcoin core implementation
if (significand <= 3) {
mantissa.shiftRight(8 * (3 - significand))
} else {
val exponent = significand - 3
val pow256 = BigInteger.valueOf(256).pow(exponent)
mantissa.multiply(pow256)
}
}
}
object NumberUtil extends NumberUtil