diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 1709ee50fb..2016ee2819 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: - version: "4.0.103" + version: "4.0.104" title: Ergo Node API description: API docs for Ergo Node. Models are shared between all Ergo products contact: diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index d125cf180b..436d20a14c 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -400,7 +400,7 @@ scorex { nodeName = "ergo-node" # Network protocol version to be sent in handshakes - appVersion = 4.0.103 + appVersion = 4.0.104 # Network agent name. May contain information about client code # stack, starting from core code-base up to the end graphical interface. diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index 4b5439078a..61d958fd94 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -76,7 +76,7 @@ scorex { network { magicBytes = [1, 0, 2, 4] bindAddress = "0.0.0.0:9030" - nodeName = "ergo-mainnet-4.0.103" + nodeName = "ergo-mainnet-4.0.104" nodeName = ${?NODENAME} knownPeers = [ "213.239.193.208:9030", diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index fdf52d4693..7bc32a2f7e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -743,10 +743,9 @@ object ErgoNodeViewHolder { history.bestFullBlockOpt .filter(_.id != lastMod.id) .fold("")(fb => s"\n best full block: $fb") - val repairNeeded = ErgoHistory.repairIfNeeded(history) + ErgoHistory.repairIfNeeded(history) // todo: do we need to do it? ChainIsStuck(s"Chain not modified for $chainUpdateDelay ms, headers-height: $headersHeight, " + - s"block-height $blockHeight, chain synced: $chainSynced, repair needed: $repairNeeded, " + - s"last modifier applied: $lastMod, " + + s"block-height $blockHeight, chain synced: $chainSynced, last modifier applied: $lastMod, " + s"possible best full block $bestFullBlockOpt") } else { ChainIsHealthy diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala index d2ff73083f..6f67de4ce8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala @@ -7,17 +7,19 @@ import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.state.UTXOSnapshotChunk -import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors._ import org.ergoplatform.nodeView.history.storage.modifierprocessors.popow.{EmptyPoPoWProofsProcessor, FullPoPoWProofsProcessor} import org.ergoplatform.settings._ import org.ergoplatform.utils.LoggingUtil +import scorex.core.consensus.ModifierSemanticValidity.Invalid import scorex.core.consensus.ProgressInfo import scorex.core.utils.NetworkTimeProvider import scorex.core.validation.RecoverableModifierError import scorex.util.{ModifierId, ScorexLogging, idToBytes} +import scala.annotation.tailrec import scala.util.{Failure, Success, Try} /** @@ -219,7 +221,7 @@ trait ErgoHistory log.info(s"Result of removing header $headerId: " + hRes) hOpt.foreach { h => - requiredModifiersForHeader(h).foreach { case (_, mId) => + h.sectionIds.foreach { case (_, mId) => val mRes = historyStorage.remove( indicesToRemove = Array(validityKey(mId)), idsToRemove = Array(mId) @@ -258,21 +260,40 @@ object ErgoHistory extends ScorexLogging { // check if there is possible database corruption when there is header after // recognized blockchain tip marked as invalid - protected[nodeView] def repairIfNeeded(history: ErgoHistory): Boolean = history.historyStorage.synchronized { + protected[nodeView] def repairIfNeeded(history: ErgoHistory): Unit = history.historyStorage.synchronized { + val RepairDepth = 128 + val bestHeaderHeight = history.headersHeight - val bestFullBlockHeight = history.bestFullBlockOpt.map(_.height).getOrElse(-1) - val afterHeaders = history.headerIdsAtHeight(bestHeaderHeight + 1) - if (bestHeaderHeight == bestFullBlockHeight && afterHeaders.nonEmpty) { - log.warn("Found suspicious continuation, clearing it...") - afterHeaders.map { hId => - history.forgetHeader(hId) + @tailrec + def checkHeightsFrom(h: Int): Unit = { + val headerIds = history.headerIdsAtHeight(h) + if (headerIds.nonEmpty) { + val notInvalidHeaders = headerIds.filter { headerId => + if (history.isSemanticallyValid(headerId) == Invalid) { + log.warn(s"Clearing invalid header: $headerId at height $h") + history.forgetHeader(headerId) + false + } else { + true + } + } + val updatedHeightIdsValue: Array[Byte] = notInvalidHeaders.foldLeft(Array.empty[Byte]) { case (acc, id) => + acc ++ idToBytes(id) + } + if(updatedHeightIdsValue.isEmpty) { + //could be the case after bestHeaderHeight + history.historyStorage.remove(Array(history.heightIdsKey(h)), Nil) + } else { + history.historyStorage.insert(Array(history.heightIdsKey(h) -> updatedHeightIdsValue), Nil) + } + checkHeightsFrom(h + 1) } - history.historyStorage.remove(Array(history.heightIdsKey(bestHeaderHeight + 1)), Nil) - true - } else { - false } + + log.info("Checking invalid headers started") + checkHeightsFrom(bestHeaderHeight - RepairDepth) + log.info("Checking invalid headers finished") } def readOrGenerate(ergoSettings: ErgoSettings, ntp: NetworkTimeProvider): ErgoHistory = {