mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-19 05:25:11 +01:00
Change ChainApi.getBestFilterHeader() return type to Future[Option[Co… (#1550)
* Change ChainApi.getBestFilterHeader() return type to Future[Option[CompactFilterHeaderDb]] to resolve issue 1549 * Run scalafmt
This commit is contained in:
parent
aa53ee5f57
commit
94568aba22
8 changed files with 132 additions and 32 deletions
|
@ -0,0 +1,20 @@
|
|||
package org.bitcoins.chain.models
|
||||
|
||||
import org.bitcoins.testkit.chain.ChainDbUnitTest
|
||||
import org.scalatest.FutureOutcome
|
||||
|
||||
class CompactFilterDAOTest extends ChainDbUnitTest {
|
||||
|
||||
override type FixtureParam = CompactFilterDAO
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withCompactFilterDAO(test)
|
||||
|
||||
behavior of "CompactFilterDAO"
|
||||
|
||||
it must "retrieve getBestFilter when there are no filters in the db" in {
|
||||
compactFilterDAO: CompactFilterDAO =>
|
||||
compactFilterDAO.getBestFilter
|
||||
.map(opt => assert(opt == None))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.bitcoins.chain.models
|
||||
|
||||
import org.bitcoins.testkit.chain.ChainDbUnitTest
|
||||
import org.scalatest.FutureOutcome
|
||||
|
||||
class CompactFilterHeaderDAOTest extends ChainDbUnitTest {
|
||||
|
||||
override type FixtureParam = CompactFilterHeaderDAO
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withCompactFilterHeaderDAO(test)
|
||||
|
||||
behavior of "CompactFilterHeaderDAO"
|
||||
|
||||
it must "get the best filter header with a table with zero rows in it" in {
|
||||
filterHeaderDAO =>
|
||||
filterHeaderDAO.getBestFilterHeader.map { opt =>
|
||||
assert(opt == None)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -123,8 +123,9 @@ trait ChainApi extends ChainQueryApi {
|
|||
/** Finds the "best" filter header we have stored in our database
|
||||
* What this means in practice is the latest filter header we
|
||||
* have received from our peer.
|
||||
* Returns none if we have no filters in the database
|
||||
* */
|
||||
def getBestFilterHeader(): Future[CompactFilterHeaderDb]
|
||||
def getBestFilterHeader(): Future[Option[CompactFilterHeaderDb]]
|
||||
|
||||
/**
|
||||
* Looks up a compact filter header by its hash.
|
||||
|
|
|
@ -352,10 +352,15 @@ case class ChainHandler(
|
|||
/** @inheritdoc */
|
||||
override def getFilterHeaderCount: Future[Int] = {
|
||||
logger.debug(s"Querying for filter header count")
|
||||
filterHeaderDAO.getBestFilter.map { filterHeader =>
|
||||
filterHeaderDAO.getBestFilterHeader.map { filterHeaderOpt =>
|
||||
filterHeaderOpt match {
|
||||
case Some(filterHeader) =>
|
||||
val height = filterHeader.height
|
||||
logger.debug(s"getFilterCount result: count=$height")
|
||||
height
|
||||
case None =>
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,8 +370,8 @@ case class ChainHandler(
|
|||
filterHeaderDAO.getAtHeight(height)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getBestFilterHeader(): Future[CompactFilterHeaderDb] = {
|
||||
filterHeaderDAO.getBestFilter
|
||||
override def getBestFilterHeader(): Future[Option[CompactFilterHeaderDb]] = {
|
||||
filterHeaderDAO.getBestFilterHeader
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
|
@ -377,10 +382,14 @@ case class ChainHandler(
|
|||
/** @inheritdoc */
|
||||
override def getFilterCount: Future[Int] = {
|
||||
logger.debug(s"Querying for filter count")
|
||||
filterDAO.getBestFilter.map { filter =>
|
||||
filterDAO.getBestFilter.map { filterOpt =>
|
||||
filterOpt match {
|
||||
case Some(filter) =>
|
||||
val height = filter.height
|
||||
logger.debug(s"getFilterCount result: count=$height")
|
||||
height
|
||||
case None => 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,16 +34,23 @@ abstract class FilterSync extends ChainVerificationLogger {
|
|||
implicit ec: ExecutionContext,
|
||||
chainAppConfig: ChainAppConfig): Future[ChainApi] = {
|
||||
|
||||
val ourBestFilterHeaderF = chainApi.getBestFilterHeader()
|
||||
val ourBestFilterHeaderOptF = chainApi.getBestFilterHeader()
|
||||
val ourBestBlockHeaderF = chainApi.getBestBlockHeader()
|
||||
for {
|
||||
ours <- ourBestFilterHeaderF
|
||||
oursOpt <- ourBestFilterHeaderOptF
|
||||
ourBestBlockHeader <- ourBestBlockHeaderF
|
||||
syncedChainApi <- syncFiltersToTip(chainApi = chainApi,
|
||||
syncedChainApi <- {
|
||||
oursOpt match {
|
||||
case Some(ours) =>
|
||||
syncFiltersToTip(chainApi = chainApi,
|
||||
ourBestHeader = ourBestBlockHeader,
|
||||
ourBestFilterHeader = ours,
|
||||
getFilterFunc = getFilterFunc,
|
||||
batchSize)
|
||||
case None =>
|
||||
Future.failed(new RuntimeException(s"Cannot sync filters, we don't have any in the database"))
|
||||
}
|
||||
}
|
||||
} yield {
|
||||
syncedChainApi
|
||||
}
|
||||
|
@ -104,11 +111,19 @@ abstract class FilterSync extends ChainVerificationLogger {
|
|||
case (apiF, missingHeaders) =>
|
||||
for {
|
||||
api <- apiF
|
||||
bestFilter <- api.getBestFilterHeader()
|
||||
newApi <- fetchFiltersForHeaderGroup(api,
|
||||
bestFilterOpt <- api.getBestFilterHeader()
|
||||
newApi <- {
|
||||
bestFilterOpt match {
|
||||
case Some(bestFilter) =>
|
||||
fetchFiltersForHeaderGroup(api,
|
||||
missingHeaders,
|
||||
bestFilter,
|
||||
getFilterFunc)
|
||||
case None =>
|
||||
Future.failed(new RuntimeException(s"Cannot sync filter headers, we do not have any in the database"))
|
||||
}
|
||||
|
||||
}
|
||||
} yield newApi
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,14 +125,25 @@ case class CompactFilterDAO()(
|
|||
table.filter(header => header.height >= from && header.height <= to).result
|
||||
}
|
||||
|
||||
def getBestFilter: Future[CompactFilterDb] = {
|
||||
val join = table join blockHeaderTable on (_.blockHash === _.hash)
|
||||
/** Gets the heaviest filter from the database */
|
||||
def getBestFilter: Future[Option[CompactFilterDb]] = {
|
||||
val join = (table.join(blockHeaderTable))
|
||||
.on(_.blockHash === _.hash)
|
||||
val query = join.groupBy(_._1).map {
|
||||
case (filter, headers) =>
|
||||
filter -> headers.map(_._2.chainWork).max
|
||||
}
|
||||
safeDatabase
|
||||
.runVec(query.result)
|
||||
.map(_.maxBy(_._2.getOrElse(BigInt(0)))._1)
|
||||
val filtersWithWorkF: Future[Vector[(CompactFilterDb,Option[BigInt])]] = {
|
||||
safeDatabase.runVec(query.result)
|
||||
}
|
||||
|
||||
filtersWithWorkF.map { filtersWithWork =>
|
||||
if (filtersWithWork.isEmpty) {
|
||||
None
|
||||
} else {
|
||||
val highest = filtersWithWork.maxBy(_._2.getOrElse(BigInt(0)))._1
|
||||
Some(highest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,15 +117,23 @@ case class CompactFilterHeaderDAO()(
|
|||
query
|
||||
}
|
||||
|
||||
def getBestFilter: Future[CompactFilterHeaderDb] = {
|
||||
def getBestFilterHeader: Future[Option[CompactFilterHeaderDb]] = {
|
||||
val join = table join blockHeaderTable on (_.blockHash === _.hash)
|
||||
val query = join.groupBy(_._1).map {
|
||||
case (filter, headers) =>
|
||||
filter -> headers.map(_._2.chainWork).max
|
||||
}
|
||||
safeDatabase
|
||||
.runVec(query.result)
|
||||
.map(_.maxBy(_._2.getOrElse(BigInt(0)))._1)
|
||||
val headersWithWorkF: Future[Vector[(CompactFilterHeaderDb,Option[BigInt])]] = {
|
||||
safeDatabase.runVec(query.result)
|
||||
}
|
||||
headersWithWorkF.map { headersWithWork: Vector[(CompactFilterHeaderDb,Option[BigInt])] =>
|
||||
if (headersWithWork.isEmpty) {
|
||||
None
|
||||
} else {
|
||||
val highestWork = headersWithWork.maxBy(_._2.getOrElse(BigInt(0)))._1
|
||||
Some(highestWork)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -155,6 +155,19 @@ trait ChainUnitTest
|
|||
destroy = () => ChainUnitTest.destroyAllTables)(test)
|
||||
}
|
||||
|
||||
/** Creates a compact filter DAO with zero rows in it */
|
||||
def withCompactFilterHeaderDAO(test: OneArgAsyncTest): FutureOutcome = {
|
||||
makeFixture(build = () => ChainUnitTest.createFilterHeaderDAO(),
|
||||
destroy = ChainUnitTest.destroyAllTables)(test)
|
||||
}
|
||||
|
||||
|
||||
/** Creates a compact filter DAO with zero rows in it */
|
||||
def withCompactFilterDAO(test: OneArgAsyncTest): FutureOutcome = {
|
||||
makeFixture(build = () => ChainUnitTest.createFilterDAO(),
|
||||
destroy = ChainUnitTest.destroyAllTables)(test)
|
||||
}
|
||||
|
||||
def withPopulatedBlockHeaderDAO(test: OneArgAsyncTest): FutureOutcome = {
|
||||
makeFixture(build = () => ChainUnitTest.createPopulatedBlockHeaderDAO,
|
||||
destroy = () => ChainUnitTest.destroyAllTables)(test)
|
||||
|
@ -313,6 +326,7 @@ object ChainUnitTest extends ChainVerificationLogger {
|
|||
def createFilterHeaderDAO()(
|
||||
implicit appConfig: ChainAppConfig,
|
||||
ec: ExecutionContext): Future[CompactFilterHeaderDAO] = {
|
||||
appConfig.migrate()
|
||||
Future.successful(CompactFilterHeaderDAO())
|
||||
}
|
||||
|
||||
|
@ -325,6 +339,7 @@ object ChainUnitTest extends ChainVerificationLogger {
|
|||
def createFilterDAO()(
|
||||
implicit appConfig: ChainAppConfig,
|
||||
ec: ExecutionContext): Future[CompactFilterDAO] = {
|
||||
appConfig.migrate()
|
||||
Future.successful(CompactFilterDAO())
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue