Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BaseTarget calculation algorithm fixing the blocktimes #94

Merged
merged 3 commits into from
Jun 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build-submodules.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to write sbt commands for such a tasks


for submodule in basics transaction consensus; do
sbt "project $submodule" "clean" "publishLocal"
done
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@ import scala.concurrent.duration._
import scala.util.{Failure, Try}


class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)
class NxtLikeConsensusModule(AvgDelay: Duration = 5.seconds)
extends LagonakiConsensusModule[NxtLikeConsensusBlockData] with ScorexLogging {

import NxtLikeConsensusModule._

implicit val consensusModule: ConsensusModule[NxtLikeConsensusBlockData] = this

val version = 1: Byte
val version = 2: Byte

val MinBlocktimeLimit = normalize(53)
val MaxBlocktimeLimit = normalize(67)
val BaseTargetGamma = normalize(64)

private val AvgDelayInSeconds: Long = AvgDelay.toSeconds
private def normalize(value: Long): Double = value * AvgDelayInSeconds / (60: Double)

override def isValid[TT](block: Block)(implicit transactionModule: TransactionModule[TT]): Boolean = Try {

Expand All @@ -37,7 +44,7 @@ class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)
val generator = block.signerDataField.value.generator

//check baseTarget
val cbt = calcBaseTarget(prevBlockData, prevTime, blockTime)
val cbt = calcBaseTarget(prev, blockTime)
val bbt = blockData.baseTarget
require(cbt == bbt, s"Block's basetarget is wrong, calculated: $cbt, block contains: $bbt")

Expand All @@ -48,7 +55,7 @@ class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)
s"Block's generation signature is wrong, calculated: ${calcGs.mkString}, block contains: ${blockGs.mkString}")

//check hit < target
calcHit(prevBlockData, generator) < calcTarget(prevBlockData, prevTime, generator)
calcHit(prevBlockData, generator) < calcTarget(prev, blockTime, effectiveBalance(generator))
}.recoverWith { case t =>
log.error("Error while checking a block", t)
Failure(t)
Expand All @@ -63,19 +70,21 @@ class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)

val lastBlockTime = lastBlock.timestampField.value

val currentTime = NTP.correctedTime()
val effBalance = effectiveBalance(account)

val h = calcHit(lastBlockKernelData, account)
val t = calcTarget(lastBlockKernelData, lastBlockTime, account)
val t = calcTarget(lastBlock, currentTime, effBalance)

val eta = (NTP.correctedTime() - lastBlockTime) / 1000
val eta = (currentTime - lastBlockTime) / 1000

log.debug(s"hit: $h, target: $t, generating ${h < t}, eta $eta, " +
s"account: $account " +
s"account balance: ${transactionModule.blockStorage.state.asInstanceOf[BalanceSheet].generationBalance(account)}"
s"account balance: $effBalance"
)

if (h < t) {
val timestamp = NTP.correctedTime()
val btg = calcBaseTarget(lastBlockKernelData, lastBlockTime, timestamp)
val btg = calcBaseTarget(lastBlock, currentTime)
val gs = calcGeneratorSignature(lastBlockKernelData, account)
val consensusData = new NxtLikeConsensusBlockData {
override val generationSignature: Array[Byte] = gs
Expand All @@ -84,9 +93,10 @@ class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)

val unconfirmed = transactionModule.packUnconfirmed()
log.debug(s"Build block with ${unconfirmed.asInstanceOf[Seq[Transaction]].size} transactions")
log.debug(s"Block time interval is $eta seconds ")

Future(Some(Block.buildAndSign(version,
timestamp,
currentTime,
lastBlock.uniqueId,
consensusData,
unconfirmed,
Expand All @@ -95,27 +105,52 @@ class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)
} else Future(None)
}

private def effectiveBalance[TT](account: Account)(implicit transactionModule: TransactionModule[TT]) =
transactionModule.blockStorage.state.asInstanceOf[BalanceSheet].balanceWithConfirmations(account.address, EffectiveBalanceDepth)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line length should be <= 120 letters


private def calcGeneratorSignature(lastBlockData: NxtLikeConsensusBlockData, generator: PublicKeyAccount) =
hash(lastBlockData.generationSignature ++ generator.publicKey)

private def calcHit(lastBlockData: NxtLikeConsensusBlockData, generator: PublicKeyAccount): BigInt =
BigInt(1, calcGeneratorSignature(lastBlockData, generator).take(8))

private def calcBaseTarget(lastBlockData: NxtLikeConsensusBlockData,
lastBlockTimestamp: Long,
currentTime: Long): Long = {
val eta = currentTime - lastBlockTimestamp
val prevBt = BigInt(lastBlockData.baseTarget)
val t0 = bounded(prevBt * eta / AvgDelay, prevBt / 2, prevBt * 2)
bounded(t0, 1, Long.MaxValue).toLong
BigInt(1, calcGeneratorSignature(lastBlockData, generator).take(8).reverse)

/**
* BaseTarget calculation algorithm fixing the blocktimes.
*/
private def calcBaseTarget[TT](prevBlock: Block, timestamp: Long)
(implicit transactionModule: TransactionModule[TT]): Long = {
val history = transactionModule.blockStorage.history
val height = history.heightOf(prevBlock).get
val prevBaseTarget = consensusBlockData(prevBlock).baseTarget
if (height % 2 == 0) {
val blocktimeAverage = (if (height > AvgBlockTimeDepth) {
(timestamp - history.parent(prevBlock, AvgBlockTimeDepth - 1).get.timestampField.value) / AvgBlockTimeDepth
} else {
timestamp - prevBlock.timestampField.value
}) / 1000

val baseTarget = (if (blocktimeAverage > AvgDelayInSeconds) {
(prevBaseTarget * Math.min(blocktimeAverage, MaxBlocktimeLimit)) / AvgDelayInSeconds
} else {
prevBaseTarget -
prevBaseTarget * BaseTargetGamma * (AvgDelayInSeconds - Math.max(blocktimeAverage, MinBlocktimeLimit)) /
(AvgDelayInSeconds * 100)
}).toLong
bounded(baseTarget, 1, Long.MaxValue).toLong
} else {
prevBaseTarget
}
}

protected def calcTarget(lastBlockData: NxtLikeConsensusBlockData,
lastBlockTimestamp: Long,
generator: PublicKeyAccount)(implicit transactionModule: TransactionModule[_]): BigInt = {
val eta = (NTP.correctedTime() - lastBlockTimestamp) / 1000 //in seconds
val effBalance = transactionModule.blockStorage.state.asInstanceOf[BalanceSheet].generationBalance(generator)
BigInt(lastBlockData.baseTarget) * eta * effBalance
private def calcTarget(prevBlock: Block,
timestamp: Long,
effBalance: Long)(implicit transactionModule: TransactionModule[_]): BigInt = {
val prevBlockData = consensusBlockData(prevBlock)
val prevBlockTimestamp = prevBlock.timestampField.value

val eta = (timestamp - prevBlockTimestamp) / 1000 //in seconds

BigInt(prevBlockData.baseTarget) * eta * effBalance
}

private def bounded(value: BigInt, min: BigInt, max: BigInt): BigInt =
Expand Down Expand Up @@ -154,4 +189,7 @@ class NxtLikeConsensusModule(AvgDelay: Long = 5.seconds.toMillis)
object NxtLikeConsensusModule {
val BaseTargetLength = 8
val GeneratorSignatureLength = 32

val EffectiveBalanceDepth: Int = 1440
val AvgBlockTimeDepth: Int = 3
}