2019 08 09 Don't use BlockHeaderDAO in TipValidation (#688)

* WIP: De-futurify TipValidatoin.chewNewTip()

* De-futrify POW and TipValidation, now tip connection is done synchronously thanks to our in memory blockchain implementation

* Fix improt issues, unused parameters
This commit is contained in:
Chris Stewart 2019-08-13 10:33:19 -05:00 committed by GitHub
parent 0abc9a77cf
commit 7caf0c355b
10 changed files with 209 additions and 227 deletions

View file

@ -25,11 +25,10 @@ class BlockchainTest extends ChainUnitTest {
val newHeader =
BlockHeaderHelper.buildNextHeader(ChainUnitTest.genesisHeaderDb)
val connectTipF = Blockchain.connectTip(header = newHeader.blockHeader,
blockHeaderDAO = bhDAO,
val connectTip = Blockchain.connectTip(header = newHeader.blockHeader,
Vector(blockchain))
connectTipF.map {
connectTip match {
case BlockchainUpdate.Successful(_, connectedHeader) =>
assert(newHeader == connectedHeader)

View file

@ -1,6 +1,7 @@
package org.bitcoins.chain.pow
import akka.actor.ActorSystem
import org.bitcoins.chain.blockchain.Blockchain
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.chain.models.BlockHeaderDAO
import org.bitcoins.core.protocol.blockchain.MainNetChainParams
@ -32,14 +33,15 @@ class BitcoinPowTest extends ChainUnitTest {
it must "NOT calculate a POW change when one is not needed" inFixtured {
case ChainFixture.Empty =>
val blockHeaderDAO = BlockHeaderDAO()
val header1 = ChainTestUtil.ValidPOWChange.blockHeaderDb566494
val blockchain = Blockchain.fromHeaders(
Vector(ChainTestUtil.ValidPOWChange.blockHeaderDb566494))
val header2 = ChainTestUtil.ValidPOWChange.blockHeaderDb566495
val nextWorkF =
Pow.getNetworkWorkRequired(header1, header2.blockHeader, blockHeaderDAO)
val nextWork =
Pow.getNetworkWorkRequired(newPotentialTip = header2.blockHeader,
blockchain = blockchain)
nextWorkF.map(nextWork => assert(nextWork == header1.nBits))
assert(nextWork == blockchain.tip.nBits)
}
it must "calculate a pow change as per the bitcoin network" inFixtured {
@ -48,13 +50,12 @@ class BitcoinPowTest extends ChainUnitTest {
val currentTipDb = ChainTestUtil.ValidPOWChange.blockHeaderDb566495
val expectedNextWork =
ChainTestUtil.ValidPOWChange.blockHeader566496.nBits
val calculatedWorkF =
val calculatedWork =
Pow.calculateNextWorkRequired(currentTipDb,
firstBlockDb,
MainNetChainParams)
calculatedWorkF.map(calculatedWork =>
assert(calculatedWork == expectedNextWork))
assert(calculatedWork == expectedNextWork)
}
it must "GetNextWorkRequired correctly" taggedAs ChainFixtureTag.PopulatedBlockHeaderDAO inFixtured {
@ -66,14 +67,15 @@ class BitcoinPowTest extends ChainUnitTest {
(ChainUnitTest.FIRST_POW_CHANGE + 1 until ChainUnitTest.FIRST_POW_CHANGE + 1 + iterations)
.map { height =>
val blockF = blockHeaderDAO.getAtHeight(height).map(_.head)
val blockchainF =
blockF.flatMap(b => blockHeaderDAO.getBlockchainFrom(b))
val nextBlockF = blockHeaderDAO.getAtHeight(height + 1).map(_.head)
for {
currentTip <- blockF
blockchain <- blockchainF
nextTip <- nextBlockF
nextNBits <- Pow.getNetworkWorkRequired(currentTip,
nextTip.blockHeader,
blockHeaderDAO)
nextNBits = Pow.getNetworkWorkRequired(nextTip.blockHeader,
blockchain)
} yield assert(nextNBits == nextTip.nBits)
}

View file

@ -18,6 +18,7 @@ import org.scalatest.{Assertion, FutureOutcome}
import scala.concurrent.Future
import org.bitcoins.chain.config.ChainAppConfig
import com.typesafe.config.ConfigFactory
import org.bitcoins.chain.blockchain.Blockchain
import org.bitcoins.server.BitcoinSAppConfig
class TipValidationTest extends ChainUnitTest {
@ -37,13 +38,14 @@ class TipValidationTest extends ChainUnitTest {
//blocks 566,092 and 566,093
val newValidTip = BlockHeaderHelper.header1
val currentTipDb = BlockHeaderHelper.header2Db
val blockchain = Blockchain.fromHeaders(Vector(currentTipDb))
it must "connect two blocks with that are valid" in { bhDAO =>
val newValidTipDb =
BlockHeaderDbHelper.fromBlockHeader(566093, newValidTip)
val expected = TipUpdateResult.Success(newValidTipDb)
runTest(newValidTip, expected, bhDAO)
runTest(newValidTip, expected, blockchain)
}
it must "fail to connect two blocks that do not reference prev block hash correctly" in {
@ -52,30 +54,27 @@ class TipValidationTest extends ChainUnitTest {
val expected = TipUpdateResult.BadPreviousBlockHash(badPrevHash)
runTest(badPrevHash, expected, bhDAO)
runTest(badPrevHash, expected, blockchain)
}
it must "fail to connect two blocks with two different POW requirements at the wrong interval" in {
bhDAO =>
val badPOW = BlockHeaderHelper.badNBits
val expected = TipUpdateResult.BadPOW(badPOW)
runTest(badPOW, expected, bhDAO)
runTest(badPOW, expected, blockchain)
}
it must "fail to connect two blocks with a bad nonce" in { bhDAO =>
val badNonce = BlockHeaderHelper.badNonce
val expected = TipUpdateResult.BadNonce(badNonce)
runTest(badNonce, expected, bhDAO)
runTest(badNonce, expected, blockchain)
}
private def runTest(
header: BlockHeader,
expected: TipUpdateResult,
blockHeaderDAO: BlockHeaderDAO,
currentTipDbDefault: BlockHeaderDb = currentTipDb): Future[Assertion] = {
val checkTipF =
TipValidation.checkNewTip(header, currentTipDbDefault, blockHeaderDAO)
checkTipF.map(validationResult => assert(validationResult == expected))
blockchain: Blockchain): Assertion = {
val result = TipValidation.checkNewTip(header, blockchain)
assert(result == expected)
}
}

View file

@ -1,13 +1,12 @@
package org.bitcoins.chain.blockchain
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.chain.models.BlockHeaderDb
import org.bitcoins.chain.validation.{TipUpdateResult, TipValidation}
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.db.ChainVerificationLogger
import scala.collection.{IndexedSeqLike, mutable}
import scala.concurrent.{ExecutionContext, Future}
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.db.ChainVerificationLogger
/**
* In memory implementation of a blockchain
@ -37,7 +36,22 @@ case class Blockchain(headers: Vector[BlockHeaderDb])
override def length: Int = headers.length
/** @inheritdoc */
override def apply(idx: Int): BlockHeaderDb = headers(idx)
override def apply(idx: Int): BlockHeaderDb = headers.apply(idx)
/** Finds a block header at a given height */
def findAtHeight(height: Int): Option[BlockHeaderDb] =
find(_.height == height)
/** Splits the blockchain at the header, returning a new blockchain where the best tip is the given header */
def fromHeader(header: BlockHeaderDb): Option[Blockchain] = {
val headerIdxOpt = headers.zipWithIndex.find(_._1 == header)
headerIdxOpt.map {
case (header, idx) =>
val newChain = Blockchain.fromHeaders(headers.splitAt(idx)._2)
require(newChain.tip == header)
newChain
}
}
}
@ -55,24 +69,18 @@ object Blockchain extends ChainVerificationLogger {
* We then attempt to connect this block header to all of our current
* chain tips.
* @param header the block header to connect to our chain
* @param blockHeaderDAO where we can find our blockchain
* @param ec
* @param blockchains the blockchain we are attempting to connect to
* @return a [[scala.concurrent.Future Future]] that contains a [[org.bitcoins.chain.blockchain.BlockchainUpdate BlockchainUpdate]] indicating
* we [[org.bitcoins.chain.blockchain.BlockchainUpdate.Successful successful]] connected the tip,
* or [[org.bitcoins.chain.blockchain.BlockchainUpdate.Failed Failed]] to connect to a tip
*/
def connectTip(
header: BlockHeader,
blockHeaderDAO: BlockHeaderDAO,
blockchains: Vector[Blockchain])(
implicit ec: ExecutionContext,
conf: ChainAppConfig): Future[BlockchainUpdate] = {
def connectTip(header: BlockHeader, blockchains: Vector[Blockchain])(
implicit conf: ChainAppConfig): BlockchainUpdate = {
logger.debug(
s"Attempting to add new tip=${header.hashBE.hex} with prevhash=${header.previousBlockHashBE.hex} to chain")
val tipResultF: Future[BlockchainUpdate] = {
val nested: Vector[Future[BlockchainUpdate]] = blockchains.map {
blockchain =>
val tipResult: BlockchainUpdate = {
val nested: Vector[BlockchainUpdate] = blockchains.map { blockchain =>
val prevBlockHeaderIdxOpt =
blockchain.headers.zipWithIndex.find {
case (headerDb, _) =>
@ -86,18 +94,16 @@ object Blockchain extends ChainVerificationLogger {
val failed = BlockchainUpdate.Failed(blockchain = blockchain,
failedHeader = header,
tipUpdateFailure = err)
Future.successful(failed)
failed
case Some((prevBlockHeader, prevHeaderIdx)) =>
//found a header to connect to!
logger.debug(
s"Attempting to add new tip=${header.hashBE.hex} with prevhash=${header.previousBlockHashBE.hex} to chain")
val tipResultF =
TipValidation.checkNewTip(newPotentialTip = header,
currentTip = prevBlockHeader,
blockHeaderDAO = blockHeaderDAO)
val chain = blockchain.fromHeader(prevBlockHeader)
val tipResult =
TipValidation.checkNewTip(newPotentialTip = header, chain.get)
tipResultF.map { tipResult =>
tipResult match {
case TipUpdateResult.Success(headerDb) =>
logger.debug(
@ -114,41 +120,36 @@ object Blockchain extends ChainVerificationLogger {
}
}
}
}
parseSuccessOrFailure(nested = nested)
parseSuccessOrFailure(nested)
}
tipResultF
tipResult
}
/** Takes in a vector of blockchain updates being executed asynchronously, we can only connect one [[BlockHeader header]]
* to a tip successfully, which means _all_ other [[BlockchainUpdate updates]] must fail. This is a helper method
* to find the one [[BlockchainUpdate.Successful successful]] update, or else returns one of the [[BlockchainUpdate.Failed failures]]
* @param nested
* @param ec
* @return
*/
private def parseSuccessOrFailure(nested: Vector[Future[BlockchainUpdate]])(
implicit ec: ExecutionContext): Future[BlockchainUpdate] = {
require(nested.nonEmpty,
private def parseSuccessOrFailure(
updates: Vector[BlockchainUpdate]): BlockchainUpdate = {
require(updates.nonEmpty,
s"Cannot parse success or failure if we don't have any updates!")
val successfulTipOptF: Future[Option[BlockchainUpdate]] = {
Future.find(nested) {
val successfulTipOpt: Option[BlockchainUpdate] = {
updates.find {
case update: BlockchainUpdate =>
update.isInstanceOf[BlockchainUpdate.Successful]
}
}
successfulTipOptF.flatMap {
case Some(update) => Future.successful(update)
successfulTipOpt match {
case Some(update) => update
case None =>
//if we didn't successfully connect a tip, just take the first failure we see
Future
.find(nested) {
updates.find {
case update: BlockchainUpdate =>
update.isInstanceOf[BlockchainUpdate.Failed]
}
.map(_.get)
}.get
}
}
}

View file

@ -3,16 +3,18 @@ package org.bitcoins.chain.blockchain
import org.bitcoins.chain.api.ChainApi
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
import org.bitcoins.chain.validation.TipUpdateResult
import org.bitcoins.chain.validation.TipUpdateResult.{
BadNonce,
BadPOW,
BadPreviousBlockHash
}
import org.bitcoins.core.crypto.DoubleSha256DigestBE
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.db.ChainVerificationLogger
import scala.concurrent.{ExecutionContext, Future}
import org.bitcoins.db.ChainVerificationLogger
import org.bitcoins.chain.validation.TipUpdateResult.BadNonce
import org.bitcoins.chain.validation.TipUpdateResult.BadPOW
import org.bitcoins.chain.validation.TipUpdateResult.BadPreviousBlockHash
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.chain.validation.TipUpdateResult
/**
* Chain Handler is meant to be the reference implementation
@ -51,12 +53,10 @@ case class ChainHandler(
logger.debug(
s"Processing header=${header.hashBE.hex}, previousHash=${header.previousBlockHashBE.hex}")
val blockchainUpdateF = Blockchain.connectTip(header = header,
blockHeaderDAO =
blockHeaderDAO,
blockchains = blockchains)
val blockchainUpdate =
Blockchain.connectTip(header = header, blockchains = blockchains)
val newHandlerF = blockchainUpdateF.flatMap {
val newHandlerF = blockchainUpdate match {
case BlockchainUpdate.Successful(newChain, updatedHeader) =>
//now we have successfully connected the header, we need to insert
//it into the database
@ -96,12 +96,6 @@ case class ChainHandler(
}
}
blockchainUpdateF.failed.foreach { err =>
logger.error(
s"Failed to connect header=${header.hashBE.hex} err=${err.getMessage}")
}
newHandlerF
}

View file

@ -184,17 +184,23 @@ case class BlockHeaderDAO()(
def getBlockchains()(
implicit ec: ExecutionContext): Future[Vector[Blockchain]] = {
val chainTipsF = chainTips
val diffInterval = appConfig.chain.difficultyChangeInterval
chainTipsF.flatMap { tips =>
val nestedFuture: Vector[Future[Blockchain]] = tips.map { tip =>
val height = Math.max(0, tip.height - diffInterval)
val headersF = getBetweenHeights(from = height, to = tip.height)
headersF.map(headers => Blockchain.fromHeaders(headers.reverse))
getBlockchainFrom(tip)
}
Future.sequence(nestedFuture)
}
}
/** Retrieves a blockchain with the best tip being the given header */
def getBlockchainFrom(header: BlockHeaderDb)(
implicit ec: ExecutionContext): Future[Blockchain] = {
val diffInterval = appConfig.chain.difficultyChangeInterval
val height = Math.max(0, header.height - diffInterval)
val headersF = getBetweenHeights(from = height, to = header.height)
headersF.map(headers => Blockchain.fromHeaders(headers.reverse))
}
/** Finds a [[org.bitcoins.chain.models.BlockHeaderDb block header]] that satisfies the given predicate, else returns None */
def find(f: BlockHeaderDb => Boolean)(
implicit ec: ExecutionContext): Future[Option[BlockHeaderDb]] = {

View file

@ -1,13 +1,12 @@
package org.bitcoins.chain.pow
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
import org.bitcoins.chain.blockchain.Blockchain
import org.bitcoins.chain.models.{BlockHeaderDb}
import org.bitcoins.core.number.UInt32
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.core.protocol.blockchain.{BlockHeader, ChainParams}
import org.bitcoins.core.util.NumberUtil
import scala.concurrent.{ExecutionContext, Future}
/**
* Implements functions found inside of bitcoin core's
* @see [[https://github.com/bitcoin/bitcoin/blob/35477e9e4e3f0f207ac6fa5764886b15bf9af8d0/src/pow.cpp pow.cpp]]
@ -17,38 +16,31 @@ sealed abstract class Pow {
/**
* Gets the next proof of work requirement for a block
* @see [[https://github.com/bitcoin/bitcoin/blob/35477e9e4e3f0f207ac6fa5764886b15bf9af8d0/src/pow.cpp#L13 Mimics bitcoin core implmentation]]
* @param tip
* @param newPotentialTip
* @return
*/
def getNetworkWorkRequired(
tip: BlockHeaderDb,
newPotentialTip: BlockHeader,
blockHeaderDAO: BlockHeaderDAO)(
implicit ec: ExecutionContext,
config: ChainAppConfig): Future[UInt32] = {
blockchain: Blockchain)(implicit config: ChainAppConfig): UInt32 = {
val chainParams = config.chain
val tip = blockchain.tip
val currentHeight = tip.height
val powLimit = NumberUtil.targetCompression(bigInteger =
chainParams.powLimit,
isNegative = false)
val powLimit: UInt32 =
if ((currentHeight + 1) % chainParams.difficultyChangeInterval != 0) {
if (chainParams.allowMinDifficultyBlocks) {
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes
// then allow mining of a min-difficulty block.
if (newPotentialTip.time.toLong > tip.blockHeader.time.toLong + chainParams.powTargetSpacing.toSeconds * 2) {
Future.successful(powLimit)
chainParams.compressedPowLimit
} else {
// Return the last non-special-min-difficulty-rules-block
//while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
// pindex = pindex->pprev;
val nonMinDiffF = blockHeaderDAO.find { h =>
h.nBits != powLimit || h.height % chainParams.difficultyChangeInterval == 0
val nonMinDiffF = blockchain.find { h =>
h.nBits != chainParams.compressedPowLimit || h.height % chainParams.difficultyChangeInterval == 0
}
nonMinDiffF.map {
nonMinDiffF match {
case Some(bh) => bh.nBits
case None =>
//if we can't find a non min diffulty block, let's just fail
@ -57,28 +49,30 @@ sealed abstract class Pow {
}
}
} else {
Future.successful(tip.blockHeader.nBits)
tip.blockHeader.nBits
}
} else {
val firstHeight = currentHeight - (chainParams.difficultyChangeInterval - 1)
val firstHeight: Int = currentHeight - (chainParams.difficultyChangeInterval - 1)
require(firstHeight >= 0,
s"We must have our first height be postive, got=${firstHeight}")
val firstBlockAtIntervalF: Future[Option[BlockHeaderDb]] = {
blockHeaderDAO.getAncestorAtHeight(tip, firstHeight)
}
val firstBlockAtIntervalOpt: Option[BlockHeaderDb] =
blockchain.findAtHeight(firstHeight)
firstBlockAtIntervalF.flatMap {
case Some(firstBlock) =>
calculateNextWorkRequired(currentTip = tip, firstBlock, chainParams)
firstBlockAtIntervalOpt match {
case Some(firstBlockAtInterval) =>
calculateNextWorkRequired(currentTip = tip,
firstBlockAtInterval,
chainParams)
case None =>
Future.failed(
new IllegalArgumentException(
s"Could not find ancestor for block=${tip.hashBE.hex}"))
throw new RuntimeException(
s"Could not find block at height=${firstHeight} out of ${blockchain.length} headers to calculate pow difficutly change")
}
}
powLimit
}
/**
@ -92,9 +86,9 @@ sealed abstract class Pow {
def calculateNextWorkRequired(
currentTip: BlockHeaderDb,
firstBlock: BlockHeaderDb,
chainParams: ChainParams): Future[UInt32] = {
chainParams: ChainParams): UInt32 = {
if (chainParams.noRetargeting) {
Future.successful(currentTip.nBits)
currentTip.nBits
} else {
var actualTimespan = (currentTip.time - firstBlock.time).toLong
val timespanSeconds = chainParams.powTargetTimeSpan.toSeconds
@ -120,7 +114,7 @@ sealed abstract class Pow {
val newTarget = NumberUtil.targetCompression(bnNew, false)
Future.successful(newTarget)
newTarget
}
}
}

View file

@ -1,16 +1,12 @@
package org.bitcoins.chain.validation
import org.bitcoins.chain.models.{
BlockHeaderDAO,
BlockHeaderDb,
BlockHeaderDbHelper
}
import org.bitcoins.chain.blockchain.Blockchain
import org.bitcoins.chain.models.{BlockHeaderDb, BlockHeaderDbHelper}
import org.bitcoins.chain.pow.Pow
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.util.NumberUtil
import scala.concurrent.{ExecutionContext, Future}
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.db.ChainVerificationLogger
@ -28,22 +24,17 @@ sealed abstract class TipValidation extends ChainVerificationLogger {
* assigned to a [[org.bitcoins.core.protocol.blockchain.BlockHeader BlockHeader]] after all these
* validation checks occur
* */
def checkNewTip(
newPotentialTip: BlockHeader,
currentTip: BlockHeaderDb,
blockHeaderDAO: BlockHeaderDAO)(
implicit ec: ExecutionContext,
conf: ChainAppConfig): Future[TipUpdateResult] = {
def checkNewTip(newPotentialTip: BlockHeader, blockchain: Blockchain)(
implicit conf: ChainAppConfig): TipUpdateResult = {
val header = newPotentialTip
val currentTip = blockchain.tip
logger.trace(
s"Checking header=${header.hashBE.hex} to try to connect to currentTip=${currentTip.hashBE.hex} with height=${currentTip.height}")
val powCheckF = isBadPow(newPotentialTip = newPotentialTip,
currentTip = currentTip,
blockHeaderDAO = blockHeaderDAO)
val expectedWork: UInt32 =
isBadPow(newPotentialTip = newPotentialTip, blockchain = blockchain)(conf)
val connectTipResultF: Future[TipUpdateResult] = {
powCheckF.map { expectedWork =>
val connectTipResult: TipUpdateResult = {
if (header.previousBlockHashBE != currentTip.hashBE) {
logger.warn(
s"Failed to connect tip=${header.hashBE.hex} to current chain")
@ -61,19 +52,16 @@ sealed abstract class TipValidation extends ChainVerificationLogger {
TipUpdateResult.Success(headerDb)
}
}
}
logTipResult(connectTipResultF, currentTip)
connectTipResultF
logTipResult(connectTipResult, currentTip)
connectTipResult
}
/** Logs the result of [[org.bitcoins.chain.validation.TipValidation.checkNewTip() checkNewTip]] */
private def logTipResult(
connectTipResultF: Future[TipUpdateResult],
currentTip: BlockHeaderDb)(
implicit ec: ExecutionContext,
conf: ChainAppConfig): Unit = {
connectTipResultF.map {
connectTipResult: TipUpdateResult,
currentTip: BlockHeaderDb)(implicit conf: ChainAppConfig): Unit = {
connectTipResult match {
case TipUpdateResult.Success(tipDb) =>
logger.trace(
s"Successfully connected ${tipDb.hashBE.hex} with height=${tipDb.height} to block=${currentTip.hashBE.hex} with height=${currentTip.height}")
@ -83,7 +71,6 @@ sealed abstract class TipValidation extends ChainVerificationLogger {
s"Failed to connect ${bad.header.hashBE.hex} to ${currentTip.hashBE.hex} with height=${currentTip.height}, reason=${bad}")
}
()
}
@ -103,15 +90,10 @@ sealed abstract class TipValidation extends ChainVerificationLogger {
}
}
private def isBadPow(
newPotentialTip: BlockHeader,
currentTip: BlockHeaderDb,
blockHeaderDAO: BlockHeaderDAO)(
implicit ec: ExecutionContext,
config: ChainAppConfig): Future[UInt32] = {
Pow.getNetworkWorkRequired(tip = currentTip,
newPotentialTip = newPotentialTip,
blockHeaderDAO = blockHeaderDAO)
private def isBadPow(newPotentialTip: BlockHeader, blockchain: Blockchain)(
config: ChainAppConfig): UInt32 = {
Pow.getNetworkWorkRequired(newPotentialTip = newPotentialTip,
blockchain = blockchain)(config)
}
}

View file

@ -2,7 +2,6 @@ package org.bitcoins.core.protocol.blockchain
import java.math.BigInteger
import java.nio.charset.StandardCharsets
import org.bitcoins.core.config.{
BitcoinNetwork,
MainNet,
@ -18,7 +17,7 @@ import org.bitcoins.core.protocol.script.{ScriptPubKey, ScriptSignature}
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant}
import org.bitcoins.core.script.crypto.OP_CHECKSIG
import org.bitcoins.core.util.BitcoinScriptUtil
import org.bitcoins.core.util.{BitcoinScriptUtil, NumberUtil}
import scodec.bits.{ByteVector, _}
import scala.concurrent.duration.{Duration, DurationInt}
@ -161,6 +160,12 @@ sealed abstract class ChainParams {
*/
def powLimit: BigInteger
/** The minimum proof of required for a block as specified by [[org.bitcoins.core.protocol.blockchain.ChainParams.powLimit powLimit]], compressed to a UInt32 */
lazy val compressedPowLimit: UInt32 = {
NumberUtil.targetCompression(bigInteger = powLimit, isNegative = false)
}
/** The targetted timespan between difficulty adjustments
* As of this implementation, all of these are the same in bitcoin core
*

View file

@ -3,7 +3,7 @@ bitcoin-s {
network = regtest # regtest, testnet3, mainnet
logging {
level = info # trace, debug, info, warn, error, off
level = INFO # trace, debug, info, warn, error, off
# You can also tune specific module loggers.
# They each take the same levels as above.