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

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

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

import cats.data.EitherT
import cats.syntax.traverse.*
import com.wavesplatform.block.Block.BlockId
import com.wavesplatform.block.{MicroBlock, MicroBlockSnapshot}
import com.wavesplatform.lang.ValidationError
import com.wavesplatform.metrics.*
import com.wavesplatform.mining.BlockChallenger
import com.wavesplatform.network.*
import com.wavesplatform.network.MicroBlockSynchronizer.MicroblockData
import com.wavesplatform.protobuf.PBSnapshots
import com.wavesplatform.state.Blockchain
import com.wavesplatform.transaction.BlockchainUpdater
import com.wavesplatform.transaction.TxValidationError.{InvalidSignature, InvalidStateHash}
import com.wavesplatform.utils.ScorexLogging
import com.wavesplatform.utx.UtxPool
import io.netty.channel.Channel
import io.netty.channel.group.ChannelGroup
import kamon.Kamon
import monix.eval.Task
import monix.execution.Scheduler

import scala.util.{Left, Right}

object MicroblockAppender extends ScorexLogging {
  private val microblockProcessingTimeStats = Kamon.timer("microblock-appender.processing-time").withoutTags()

  def apply(blockchainUpdater: BlockchainUpdater & Blockchain, utxStorage: UtxPool, scheduler: Scheduler, verify: Boolean = true)(
      microBlock: MicroBlock,
      snapshot: Option[MicroBlockSnapshot]
  ): Task[Either[ValidationError, BlockId]] =
    Task(microblockProcessingTimeStats.measureSuccessful {
      blockchainUpdater
        .processMicroBlock(microBlock, snapshot, verify)
        .map { totalBlockId =>
          if (microBlock.transactionData.nonEmpty) {
            utxStorage.removeAll(microBlock.transactionData)
            log.trace(
              s"Removing txs of ${microBlock.stringRepr(totalBlockId)} ${microBlock.transactionData.map(_.id()).mkString("(", ", ", ")")} from UTX pool"
            )
          }

          utxStorage.scheduleCleanup()
          totalBlockId
        }
    }).executeOn(scheduler)

  def apply(
      blockchainUpdater: BlockchainUpdater & Blockchain,
      utxStorage: UtxPool,
      allChannels: ChannelGroup,
      peerDatabase: PeerDatabase,
      blockChallenger: Option[BlockChallenger],
      scheduler: Scheduler
  )(ch: Channel, md: MicroblockData, snapshot: Option[(Channel, MicroBlockSnapshotResponse)]): Task[Unit] = {
    import md.microBlock
    val microblockTotalResBlockSig = microBlock.totalResBlockSig
    (for {
      _ <- EitherT(Task.now(microBlock.signaturesValid()))
      microBlockSnapshot = snapshot
        .map { case (_, mbs) =>
          microBlock.transactionData.zip(mbs.snapshots).map { case (tx, pbs) =>
            PBSnapshots.fromProtobuf(pbs, tx.id(), blockchainUpdater.height)
          }
        }
        .map(ss => MicroBlockSnapshot(microblockTotalResBlockSig, ss))

      blockId <- EitherT(apply(blockchainUpdater, utxStorage, scheduler)(microBlock, microBlockSnapshot))
    } yield blockId).value.flatMap {
      case Right(blockId) =>
        Task {
          md.invOpt match {
            case Some(mi) => allChannels.broadcast(mi, except = md.microblockOwners())
            case None     => log.warn(s"${id(ch)} Not broadcasting MicroBlockInv")
          }
          BlockStats.applied(microBlock, blockId)
        }
      case Left(is: InvalidSignature) =>
        Task {
          val idOpt = md.invOpt.map(_.totalBlockId)
          peerDatabase.blacklistAndClose(ch, s"Could not append microblock ${idOpt.getOrElse(s"(sig=$microblockTotalResBlockSig)")}: $is")
        }
      case Left(ish: InvalidStateHash) =>
        val channelToBlacklist = snapshot.map(_._1).getOrElse(ch)
        val idOpt              = md.invOpt.map(_.totalBlockId)
        peerDatabase.blacklistAndClose(
          channelToBlacklist,
          s"Could not append microblock ${idOpt.getOrElse(s"(sig=$microblockTotalResBlockSig)")}: $ish"
        )
        md.invOpt.foreach(mi => BlockStats.declined(mi.totalBlockId))

        blockChallenger.traverse(_.challengeMicroblock(md, channelToBlacklist).executeOn(scheduler)).void

      case Left(ve) =>
        Task {
          md.invOpt.foreach(mi => BlockStats.declined(mi.totalBlockId))
          val idOpt = md.invOpt.map(_.totalBlockId)
          log.debug(s"${id(ch)} Could not append microblock ${idOpt.getOrElse(s"(sig=$microblockTotalResBlockSig)")}: $ve")
        }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy