mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
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:
parent
0abc9a77cf
commit
7caf0c355b
10 changed files with 209 additions and 227 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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]] = {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue