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

org.alephium.protocol.model.Target.scala Maven / Gradle / Ivy

There is a newer version: 3.8.8
Show newest version
// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .

package org.alephium.protocol.model

import java.math.BigInteger

import akka.util.ByteString

import org.alephium.protocol.config.GroupConfig
import org.alephium.protocol.mining.HashRate
import org.alephium.serde._
import org.alephium.util.{AVector, Bytes, Duration, Hex, Number}

/*
 * value = mantissa * 256 ^ (exponent - 3)
 * value should not be negative always
 */
final case class Target(bits: ByteString) extends Ordered[Target] {
  lazy val value: BigInteger = Target.fromCompactBitsUnsafe(bits)

  def getDifficulty(): Difficulty = Difficulty.unsafe(Target.maxBigInt.divide(value))

  override def compare(that: Target): Int = this.value.compareTo(that.value)

  def toHexString: String = Hex.toHexString(bits)

  override def toString: String = s"Target($toHexString)"
}

object Target {
  implicit val serde: Serde[Target] = Serde.bytesSerde(4).xmap(unsafe, _.bits)

  val maxBigInt: BigInteger = BigInteger.ONE.shiftLeft(256)

  val Max: Target  = unsafe(maxBigInt.subtract(BigInteger.ONE))
  val Zero: Target = unsafe(BigInteger.ZERO)

  def unsafe(byteString: ByteString): Target = {
    require(byteString.length == 4)
    new Target(byteString)
  }

  def unsafe(value: BigInteger): Target = {
    require(Number.nonNegative(value) && value.compareTo(maxBigInt) < 0)
    new Target(toCompactBitsUnsafe(value))
  }

  // scalastyle:off magic.number
  def from(hashRate: HashRate, blockTime: Duration)(implicit groupConfig: GroupConfig): Target = {
    val hashNeeded =
      hashRate.value
        .multiply(BigInteger.valueOf(blockTime.millis))
        // divide the hashrate due to: multiple chains and chain index encoding in block hash
        .divide(BigInteger.valueOf((1000 * groupConfig.chainNum * groupConfig.chainNum).toLong))
    Target.unsafe(Target.maxBigInt.divide(hashNeeded))
  }
  // scalastyle:on magic.number

  @SuppressWarnings(Array("org.wartremover.warts.IterableOps"))
  def fromCompactBitsUnsafe(bits: ByteString): BigInteger = {
    assume(bits.length == 4)
    val size: Int            = Bytes.toPosInt(bits(0))
    val mantissa: BigInteger = new BigInteger(1, bits.tail.toArray)
    if (size <= 3) {
      mantissa.shiftRight(8 * (3 - size))
    } else {
      mantissa.shiftLeft(8 * (size - 3))
    }
  }

  @SuppressWarnings(Array("org.wartremover.warts.IterableOps"))
  def toCompactBitsUnsafe(value: BigInteger): ByteString = {
    val size: Int = (value.bitLength() + 7) / 8
    val mantissa = if (size <= 3) {
      Bytes.from(value.intValue() << (8 * (3 - size)))
    } else {
      Bytes.from(value.shiftRight(8 * (size - 3)).intValue())
    }
    ByteString(size.toByte) ++ mantissa.tail
  }

  // The final target is the weighted average of the adjusted target and deps targets
  // average == (selfTarget * (2G+1) + sum(depTarget)) / 4G
  def average(newTarget: Target, depTargets: AVector[Target])(implicit
      groupConfig: GroupConfig
  ): Target = {
    val selfWeight         = 2 * groupConfig.groups + 1
    val selfWeightedTarget = newTarget.value.multiply(BigInteger.valueOf(selfWeight.toLong))
    val totalDepsTarget    = depTargets.fold(BigInteger.ZERO)(_ add _.value)
    val average = (selfWeightedTarget add totalDepsTarget).divide(groupConfig.targetAverageCount)
    Target.unsafe(average)
  }

  def clipByTwoTimes(maxTarget: Target, newTarget: Target): Target = {
    assume(maxTarget >= newTarget)
    val lowerBound = maxTarget.value.shiftRight(1)
    if (newTarget.value.compareTo(lowerBound) < 0) {
      Target.unsafe(lowerBound)
    } else {
      newTarget
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy