diff --git a/chain-test/src/test/scala/org/bitcoins/chain/blockchain/ChainHandlerTest.scala b/chain-test/src/test/scala/org/bitcoins/chain/blockchain/ChainHandlerTest.scala index 543970b27e..2180da6fad 100644 --- a/chain-test/src/test/scala/org/bitcoins/chain/blockchain/ChainHandlerTest.scala +++ b/chain-test/src/test/scala/org/bitcoins/chain/blockchain/ChainHandlerTest.scala @@ -94,6 +94,16 @@ class ChainHandlerTest extends ChainDbUnitTest { } yield succeed } + it must "fail if we give a header that cannot be connected to anything" in { + chainHandler: ChainHandler => + val newHeader = BlockHeaderHelper.badPrevHash + recoverToSucceededIf[RuntimeException] { + for { + result <- chainHandler.processHeader(newHeader) + } yield result + } + } + // B // C -> D it must "handle a very basic reorg where one chain is one block behind the best chain" in { diff --git a/chain/src/main/scala/org/bitcoins/chain/blockchain/ChainHandler.scala b/chain/src/main/scala/org/bitcoins/chain/blockchain/ChainHandler.scala index 848f0e3e4c..349353dbe2 100644 --- a/chain/src/main/scala/org/bitcoins/chain/blockchain/ChainHandler.scala +++ b/chain/src/main/scala/org/bitcoins/chain/blockchain/ChainHandler.scala @@ -135,35 +135,44 @@ class ChainHandler( successfullyValidatedHeaders.distinct } - val chains = blockchainUpdates.map(_.blockchain) + if (headersToBeCreated.isEmpty) { + //this means we are given zero headers that were valid. + //Return a failure in this case to avoid issue 2365 + //https://github.com/bitcoin-s/bitcoin-s/issues/2365 + Future.failed(new RuntimeException( + s"Failed to connect any headers to our internal chain state, failures=$blockchainUpdates")) + } else { + val chains = blockchainUpdates.map(_.blockchain) - val createdF = blockHeaderDAO.createAll(headersToBeCreated) + val createdF = blockHeaderDAO.createAll(headersToBeCreated) - val newChainHandler = ChainHandler(blockHeaderDAO, - filterHeaderDAO, - filterDAO, - blockFilterCheckpoints = - blockFilterCheckpoints) + val newChainHandler = ChainHandler(blockHeaderDAO, + filterHeaderDAO, + filterDAO, + blockFilterCheckpoints = + blockFilterCheckpoints) - createdF.map { headers => - if (chainConfig.chainCallbacks.onBlockHeaderConnected.nonEmpty) { - headersToBeCreated.reverseIterator.foldLeft(FutureUtil.unit) { - (acc, header) => - for { - _ <- acc - _ <- - chainConfig.chainCallbacks - .executeOnBlockHeaderConnectedCallbacks(logger, - header.height, - header.blockHeader) - } yield () + createdF.map { headers => + if (chainConfig.chainCallbacks.onBlockHeaderConnected.nonEmpty) { + headersToBeCreated.reverseIterator.foldLeft(FutureUtil.unit) { + (acc, header) => + for { + _ <- acc + _ <- + chainConfig.chainCallbacks + .executeOnBlockHeaderConnectedCallbacks( + logger, + header.height, + header.blockHeader) + } yield () + } } + chains.foreach { c => + logger.info( + s"Processed headers from height=${c.height - headers.length} to ${c.height}. Best hash=${c.tip.hashBE.hex}") + } + newChainHandler } - chains.foreach { c => - logger.info( - s"Processed headers from height=${c.height - headers.length} to ${c.height}. Best hash=${c.tip.hashBE.hex}") - } - newChainHandler } } } diff --git a/core/src/main/scala/org/bitcoins/core/api/chain/ChainApi.scala b/core/src/main/scala/org/bitcoins/core/api/chain/ChainApi.scala index 53d63e6344..8ba5b74254 100644 --- a/core/src/main/scala/org/bitcoins/core/api/chain/ChainApi.scala +++ b/core/src/main/scala/org/bitcoins/core/api/chain/ChainApi.scala @@ -19,7 +19,9 @@ import scala.concurrent.Future trait ChainApi extends ChainQueryApi { /** - * Adds a block header to our chain project + * Adds a block header to our chain project. + * This will return a failed future when the + * given header is invalid. * @param header * @return */ @@ -29,7 +31,9 @@ trait ChainApi extends ChainQueryApi { /** Process all of the given headers and returns a new [[ChainApi chain api]] * that contains these headers. This method processes headers in the order - * that they are given. If the headers are out of order, this method will fail + * that they are given. If the headers are out of order, this method will fail. + * + * This method will also fail when there are zero headers given that are valid. * @param headers * @return */ diff --git a/node-test/src/test/scala/org/bitcoins/node/networking/peer/DataMessageHandlerTest.scala b/node-test/src/test/scala/org/bitcoins/node/networking/peer/DataMessageHandlerTest.scala index 1dbe943e7c..4dc1e99a79 100644 --- a/node-test/src/test/scala/org/bitcoins/node/networking/peer/DataMessageHandlerTest.scala +++ b/node-test/src/test/scala/org/bitcoins/node/networking/peer/DataMessageHandlerTest.scala @@ -105,7 +105,9 @@ class DataMessageHandlerTest extends NodeUnitTest { val callback: OnBlockHeadersReceived = (headers: Vector[BlockHeader]) => { Future { - resultP.success(headers) + if (!resultP.isCompleted) { + resultP.success(headers) + } () } }