All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavesplatform.state.appender.ExtensionAppender.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.state.appender

import com.wavesplatform.common.utils.EitherExt2
import com.wavesplatform.consensus.PoSSelector
import com.wavesplatform.features.BlockchainFeatures
import com.wavesplatform.lang.ValidationError
import com.wavesplatform.metrics.{BlockStats, Metrics}
import com.wavesplatform.network.{ExtensionBlocks, InvalidBlockStorage, PeerDatabase, formatBlocks, id}
import com.wavesplatform.state.*
import com.wavesplatform.state.BlockchainUpdaterImpl.BlockApplyResult.Applied
import com.wavesplatform.transaction.*
import com.wavesplatform.transaction.TxValidationError.GenericError
import com.wavesplatform.utils.{ScorexLogging, Time}
import com.wavesplatform.utx.UtxPool
import io.netty.channel.Channel
import monix.eval.Task
import monix.execution.Scheduler
import org.influxdb.dto.Point

import scala.util.{Left, Right}

object ExtensionAppender extends ScorexLogging {

  def apply(
      blockchainUpdater: BlockchainUpdater & Blockchain,
      utxStorage: UtxPool,
      pos: PoSSelector,
      time: Time,
      invalidBlocks: InvalidBlockStorage,
      peerDatabase: PeerDatabase,
      scheduler: Scheduler
  )(ch: Channel, extensionBlocks: ExtensionBlocks): Task[Either[ValidationError, Option[BigInt]]] = {
    def appendExtension(extension: ExtensionBlocks): Either[ValidationError, Option[BigInt]] =
      if (extension.remoteScore <= blockchainUpdater.score) {
        log.trace(s"Ignoring extension $extension because declared remote was not greater than local score ${blockchainUpdater.score}")
        Right(None)
      } else {
        extension.blocks
          .collectFirst { case b if !b.signatureValid() => GenericError(s"Block $b has invalid signature") }
          .toLeft(extension)
          .flatMap { extensionWithValidSignatures =>
            val newBlocks = extensionWithValidSignatures.blocks.dropWhile(blockchainUpdater.contains)

            newBlocks.headOption.map(_.header.reference) match {
              case Some(lastCommonBlockId) =>
                val initialHeight = blockchainUpdater.height

                val droppedBlocksEi = for {
                  commonBlockHeight <- blockchainUpdater.heightOf(lastCommonBlockId).toRight(GenericError("Fork contains no common parent"))
                  droppedBlocks <- {
                    if (commonBlockHeight < initialHeight)
                      blockchainUpdater.removeAfter(lastCommonBlockId)
                    else Right(Seq.empty)
                  }
                } yield (commonBlockHeight, droppedBlocks)

                droppedBlocksEi.flatMap { case (commonBlockHeight, droppedBlocks) =>
                  newBlocks.zipWithIndex.foreach { case (block, idx) =>
                    val rideV6Activated = blockchainUpdater.isFeatureActivated(BlockchainFeatures.RideV6, commonBlockHeight + idx + 1)
                    ParSignatureChecker.checkTxSignatures(block.transactionData, rideV6Activated)
                  }

                  val forkApplicationResultEi = {
                    newBlocks.view
                      .map { b =>
                        b -> appendExtensionBlock(blockchainUpdater, pos, time, verify = true, txSignParCheck = false)(
                          b,
                          extension.snapshots.get(b.id())
                        )
                          .map {
                            case (Applied(_, _), height) => BlockStats.applied(b, BlockStats.Source.Ext, height)
                            case _                       =>
                          }
                      }
                      .zipWithIndex
                      .collectFirst { case ((b, Left(e)), i) => (i, b, e) }
                      .fold[Either[ValidationError, Unit]](Right(())) { case (i, declinedBlock, e) =>
                        e match {
                          case _: TxValidationError.BlockFromFuture =>
                          case _                                    => invalidBlocks.add(declinedBlock.id(), e)
                        }

                        newBlocks.view
                          .dropWhile(_ != declinedBlock)
                          .foreach(BlockStats.declined(_, BlockStats.Source.Ext))

                        if (i == 0) log.warn(s"Can't process fork starting with $lastCommonBlockId, error appending block $declinedBlock: $e")
                        else
                          log.warn(
                            s"Processed only ${i + 1} of ${newBlocks.size} blocks from extension, error appending next block $declinedBlock: $e"
                          )

                        Left(e)
                      }
                  }

                  forkApplicationResultEi match {
                    case Left(e) =>
                      blockchainUpdater.removeAfter(lastCommonBlockId).explicitGet()
                      droppedBlocks.foreach { case (b, gp, sn) => blockchainUpdater.processBlock(b, gp, sn).explicitGet() }
                      Left(e)

                    case Right(_) =>
                      val depth = initialHeight - commonBlockHeight
                      if (depth > 0) {
                        Metrics.write(
                          Point
                            .measurement("rollback")
                            .addField("depth", initialHeight - commonBlockHeight)
                            .addField("txs", droppedBlocks.size)
                        )
                      }

                      val newTransactions = newBlocks.view.flatMap(_.transactionData).toSet
                      utxStorage.removeAll(newTransactions)
                      utxStorage.addAndScheduleCleanup(droppedBlocks.flatMap(_._1.transactionData).filterNot(newTransactions))
                      Right(Some(blockchainUpdater.score))
                  }
                }

              case None =>
                log.debug("No new blocks found in extension")
                Right(None)
            }
          }
      }

    log.debug(s"${id(ch)} Attempting to append extension ${formatBlocks(extensionBlocks.blocks)}")
    Task(appendExtension(extensionBlocks)).executeOn(scheduler).map {
      case Right(maybeNewScore) =>
        log.debug(s"${id(ch)} Successfully appended extension ${formatBlocks(extensionBlocks.blocks)}")
        Right(maybeNewScore)
      case Left(ve) =>
        val errorMessage = s"${id(ch)} Error appending extension ${formatBlocks(extensionBlocks.blocks)}: $ve"
        log.warn(errorMessage)
        peerDatabase.blacklistAndClose(ch, errorMessage)
        Left(ve)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy