2020 08 14 issue 1829 (#1833)

* Fix O(n^2) behavior in BaseBlockchain.connectWalkBackwards, cache BlockHeader.hash after the first time it is called

* Revert a few files
This commit is contained in:
Chris Stewart 2020-08-15 07:31:50 -05:00 committed by GitHub
parent 48e3cb8e14
commit c9f78a2a40
3 changed files with 35 additions and 19 deletions

View file

@ -597,14 +597,20 @@ class ChainHandlerTest extends ChainDbUnitTest {
getHeaderF.map(headerOpt =>
assert(headerOpt.contains(expectedBlockHeaderDb)))
val newAccum = accum.:+(assertionF)
loop(headersTail, Some(expectedBlockHeaderDb), height + 1, newAccum)
loop(remainingHeaders = headersTail,
prevHeaderDbOpt = Some(expectedBlockHeaderDb),
height = height + 1,
accum = newAccum)
case Vector() =>
accum
}
}
val vecFutAssert: Vector[Future[Assertion]] =
loop(headers, None, height, Vector.empty)
loop(remainingHeaders = headers,
prevHeaderDbOpt = None,
height = height,
accum = Vector.empty)
ScalaTestUtil.toAssertF(vecFutAssert)
}

View file

@ -239,22 +239,33 @@ private[blockchain] trait BaseBlockChainCompObject
/** Walks backwards from the current header searching through ancestors if [[current.previousBlockHashBE]] is in [[ancestors]]
* This does not validate other things such as POW.
*/
@tailrec
final def connectWalkBackwards(
current: BlockHeaderDb,
ancestors: Vector[BlockHeaderDb],
accum: Vector[BlockHeaderDb] = Vector.empty)(implicit
chainAppConfig: ChainAppConfig): Vector[BlockHeaderDb] = {
val prevHeaderOpt = ancestors.find(_.hashBE == current.previousBlockHashBE)
prevHeaderOpt match {
case Some(h) =>
connectWalkBackwards(current = h,
accum = current +: accum,
ancestors = ancestors)
case None =>
logger.debug(s"No prev found for $current hashBE=${current.hashBE}")
current +: accum
ancestors: Vector[BlockHeaderDb]): Vector[BlockHeaderDb] = {
val groupByHeight: Map[Int, Vector[BlockHeaderDb]] = {
ancestors.groupBy(_.height)
}
@tailrec
def loop(
current: BlockHeaderDb,
accum: Vector[BlockHeaderDb]): Vector[BlockHeaderDb] = {
val prevHeight = current.height - 1
val possibleHeadersOpt: Option[Vector[BlockHeaderDb]] =
groupByHeight.get(prevHeight)
val prevHeaderOpt = possibleHeadersOpt.flatMap(
_.find(_.hashBE == current.previousBlockHashBE)
)
prevHeaderOpt match {
case Some(prevHeader) =>
loop(prevHeader, current +: accum)
case None =>
current +: accum
}
}
loop(current, Vector.empty)
}
/** Walks backwards from a child header reconstructing a blockchain
@ -266,9 +277,8 @@ private[blockchain] trait BaseBlockChainCompObject
chainAppConfig: ChainAppConfig): Vector[Blockchain] = {
//now all hashes are connected correctly forming a
//valid blockchain in term of hashes connected to each other
val orderedHeaders = connectWalkBackwards(current = childHeader,
accum = Vector.empty,
ancestors = ancestors)
val orderedHeaders =
connectWalkBackwards(current = childHeader, ancestors = ancestors)
val initBlockchainOpt = orderedHeaders match {
case Vector() | _ +: Vector() =>

View file

@ -116,7 +116,7 @@ sealed trait BlockHeader extends NetworkElement {
def nonce: UInt32
/** Returns the block's hash in the protocol level little endian encoding */
def hash: DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
lazy val hash: DoubleSha256Digest = CryptoUtil.doubleSHA256(bytes)
/**
* Returns the block hash in big endian format, this is useful for rpc