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

com.wavesplatform.consensus.PoSCalculator.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.consensus

import com.wavesplatform.account.{PrivateKey, PublicKey}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.consensus.PoSCalculator.HitSize
import com.wavesplatform.crypto
import com.wavesplatform.settings.FunctionalitySettings

import scala.util.Try

sealed trait PoSCalculator {
  def calculateBaseTarget(
      targetBlockDelaySeconds: Long,
      prevHeight: Int,
      prevBaseTarget: Long,
      parentTimestamp: Long,
      maybeGreatGrandParentTimestamp: Option[Long],
      timestamp: Long
  ): Long

  def calculateDelay(hit: BigInt, bt: Long, balance: Long): Long
}

object PoSCalculator {
  val HitSize: Int        = 8
  val MinBaseTarget: Long = 9

  def generationSignature(signature: ByteStr, publicKey: PublicKey): Array[Byte] = {
    val s = new Array[Byte](crypto.DigestLength * 2)
    System.arraycopy(signature.arr, 0, s, 0, crypto.DigestLength)
    System.arraycopy(publicKey.arr, 0, s, crypto.DigestLength, crypto.DigestLength)
    crypto.fastHash(s)
  }

  private[consensus] def generationVRFSignature(signature: Array[Byte], privateKey: PrivateKey): ByteStr =
    crypto.signVRF(privateKey, signature)

  def hit(generatorSignature: Array[Byte]): BigInt = BigInt(1, generatorSignature.take(HitSize).reverse)

  private[consensus] def normalize(value: Long, targetBlockDelaySeconds: Long): Double =
    value * targetBlockDelaySeconds / (60: Double)

  private[consensus] def normalizeBaseTarget(baseTarget: Long, targetBlockDelaySeconds: Long): Long = {
    baseTarget
      .max(MinBaseTarget)
      .min(Long.MaxValue / targetBlockDelaySeconds)
  }
}

object NxtPoSCalculator extends PoSCalculator {
  protected val MinBlockDelaySeconds = 53
  protected val MaxBlockDelaySeconds = 67
  protected val BaseTargetGamma      = 64
  protected val MeanCalculationDepth = 3

  import PoSCalculator.*

  def calculateBaseTarget(
      targetBlockDelaySeconds: Long,
      prevHeight: Int,
      prevBaseTarget: Long,
      parentTimestamp: Long,
      maybeGreatGrandParentTimestamp: Option[Long],
      timestamp: Long
  ): Long = {

    if (prevHeight % 2 == 0) {
      val meanBlockDelay  = maybeGreatGrandParentTimestamp.fold(timestamp - parentTimestamp)(ts => (timestamp - ts) / MeanCalculationDepth) / 1000
      val minBlockDelay   = normalize(MinBlockDelaySeconds, targetBlockDelaySeconds)
      val maxBlockDelay   = normalize(MaxBlockDelaySeconds, targetBlockDelaySeconds)
      val baseTargetGamma = normalize(BaseTargetGamma, targetBlockDelaySeconds)

      val baseTarget = (if (meanBlockDelay > targetBlockDelaySeconds) {
                          prevBaseTarget * Math.min(meanBlockDelay.toDouble, maxBlockDelay) / targetBlockDelaySeconds
                        } else {
                          prevBaseTarget - prevBaseTarget * baseTargetGamma *
                            (targetBlockDelaySeconds - Math.max(meanBlockDelay.toDouble, minBlockDelay)) / (targetBlockDelaySeconds * 100)
                        }).toLong

      normalizeBaseTarget(baseTarget, targetBlockDelaySeconds)
    } else {
      prevBaseTarget
    }
  }

  def calculateDelay(hit: BigInt, bt: Long, balance: Long): Long = Math.ceil((BigDecimal(hit) / (BigDecimal(bt) * balance)).toDouble).toLong * 1000
}

object FairPoSCalculator {
  lazy val V1: FairPoSCalculator = FairPoSCalculator(5000, 0)
  lazy val V2: FairPoSCalculator = FairPoSCalculator(15000, 8)

  def fromSettings(fs: FunctionalitySettings): PoSCalculator =
    if (fs.minBlockTime.toSeconds == 15 && fs.delayDelta == 8) V2
    else FairPoSCalculator(fs.minBlockTime.toMillis.toInt, fs.delayDelta)

  val MaxHit: BigDecimal = BigDecimal(BigInt(1, Array.fill[Byte](HitSize)(-1)))
  private val C1         = 70000
  private val C2         = 5e17
}

case class FairPoSCalculator(minBlockTime: Int, delayDelta: Int) extends PoSCalculator {
  import FairPoSCalculator.*
  import PoSCalculator.*

  def calculateDelay(hit: BigInt, bt: Long, balance: Long): Long = {
    val h = (BigDecimal(hit) / MaxHit).toDouble
    val a = minBlockTime + C1 * math.log(1 - C2 * math.log(h) / bt / balance)
    a.toLong
  }

  def calculateBaseTarget(
      targetBlockDelaySeconds: Long,
      prevHeight: Int,
      prevBaseTarget: Long,
      parentTimestamp: Long,
      maybeGreatGrandParentTimestamp: Option[Long],
      timestamp: Long
  ): Long = {
    val maxDelay = normalize(90 - delayDelta, targetBlockDelaySeconds)
    val minDelay = normalize(30 + delayDelta, targetBlockDelaySeconds)

    maybeGreatGrandParentTimestamp match {
      case None =>
        prevBaseTarget

      case Some(ts) =>
        val avg                = (timestamp - ts) / 3 / 1000
        val prevBaseTarget1pct = (prevBaseTarget / 100) max 1
        if (avg > maxDelay) Try(math.addExact(prevBaseTarget, prevBaseTarget1pct)).getOrElse(Long.MaxValue)
        else if (avg < minDelay) math.subtractExact(prevBaseTarget, prevBaseTarget1pct) max 1
        else prevBaseTarget
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy