Skip to content

Commit

Permalink
input/ordering block distintcion in mining logic #1, input block gene…
Browse files Browse the repository at this point in the history
…ration
  • Loading branch information
kushti committed Sep 19, 2024
1 parent e93d333 commit e0feb83
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.ergoplatform.mining
import com.google.common.primitives.{Bytes, Ints, Longs}
import org.bouncycastle.util.BigIntegers
import org.ergoplatform.ErgoLikeContext.Height
import org.ergoplatform.{InputBlockFound, InputBlockHeaderFound, InputSolutionFound, NothingFound, OrderingBlockFound, OrderingBlockHeeaderFound, OrderingSolutionFound, ProveBlockResult}
import org.ergoplatform.mining.difficulty.DifficultySerializer
import org.ergoplatform.modifiers.ErgoFullBlock
import org.ergoplatform.modifiers.history._
Expand Down Expand Up @@ -278,6 +279,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {

//Proving-related code which is not critical for consensus below


/**
* Autolykos solver suitable for CPU-mining in testnet and devnets.
*
Expand All @@ -295,7 +297,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
votes: Array[Byte],
sk: PrivateKey,
minNonce: Long = Long.MinValue,
maxNonce: Long = Long.MaxValue): Option[Header] = {
maxNonce: Long = Long.MaxValue): ProveBlockResult = {
val (parentId, height) = AutolykosPowScheme.derivedHeaderFields(parentOpt)

val h = HeaderWithoutPow(version, parentId, adProofsRoot, stateRoot, transactionsRoot, timestamp,
Expand All @@ -305,7 +307,11 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
val x = randomSecret()
val hbs = Ints.toByteArray(h.height)
val N = calcN(h)
checkNonces(version, hbs, msg, sk, x, b, N, minNonce, maxNonce).map(solution => h.toHeader(solution))
checkNonces(version, hbs, msg, sk, x, b, N, minNonce, maxNonce) match {
case NothingFound => NothingFound
case InputSolutionFound(as) => InputBlockHeaderFound(h.toHeader(as))
case OrderingSolutionFound(as) => OrderingBlockHeeaderFound(h.toHeader(as))
}
}

/**
Expand All @@ -323,18 +329,24 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
votes: Array[Byte],
sk: PrivateKey,
minNonce: Long = Long.MinValue,
maxNonce: Long = Long.MaxValue): Option[ErgoFullBlock] = {
maxNonce: Long = Long.MaxValue): ProveBlockResult = {

val transactionsRoot = BlockTransactions.transactionsRoot(transactions, version)
val adProofsRoot = ADProofs.proofDigest(adProofBytes)

prove(parentOpt, version, nBits, stateRoot, adProofsRoot, transactionsRoot,
timestamp, extensionCandidate.digest, votes, sk, minNonce, maxNonce).map { h =>
def constructBlockFromHeader(h: Header) = {
val adProofs = ADProofs(h.id, adProofBytes)
val blockTransactions = BlockTransactions(h.id, version, transactions)
val extension = extensionCandidate.toExtension(h.id)
new ErgoFullBlock(h, blockTransactions, extension, Some(adProofs))
}

prove(parentOpt, version, nBits, stateRoot, adProofsRoot, transactionsRoot,
timestamp, extensionCandidate.digest, votes, sk, minNonce, maxNonce) match {
case NothingFound => NothingFound
case InputBlockHeaderFound(h) => InputBlockFound(constructBlockFromHeader(h))
case OrderingBlockHeeaderFound(h) => OrderingBlockFound(constructBlockFromHeader(h))
}
}

/**
Expand All @@ -344,7 +356,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
def proveCandidate(candidateBlock: CandidateBlock,
sk: PrivateKey,
minNonce: Long = Long.MinValue,
maxNonce: Long = Long.MaxValue): Option[ErgoFullBlock] = {
maxNonce: Long = Long.MaxValue): ProveBlockResult = {
proveBlock(candidateBlock.parentOpt,
candidateBlock.version,
candidateBlock.nBits,
Expand Down Expand Up @@ -372,14 +384,17 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
b: BigInt,
N: Int,
startNonce: Long,
endNonce: Long): Option[AutolykosSolution] = {
endNonce: Long): ProveBlockResult = {

val subblocksPerBlock = 10 // todo : make configurable

log.debug(s"Going to check nonces from $startNonce to $endNonce")
val p1 = groupElemToBytes(genPk(sk))
val p2 = groupElemToBytes(genPk(x))

@tailrec
def loop(i: Long): Option[AutolykosSolution] = if (i == endNonce) {
None
def loop(i: Long): ProveBlockResult = if (i == endNonce) {
NothingFound
} else {
if (i % 1000000 == 0 && i > 0) println(s"$i nonce tested")
val nonce = Longs.toByteArray(i)
Expand All @@ -398,7 +413,9 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
}
if (d <= b) {
log.debug(s"Solution found at $i")
Some(AutolykosSolution(genPk(sk), genPk(x), nonce, d))
OrderingSolutionFound(AutolykosSolution(genPk(sk), genPk(x), nonce, d))
} else if (d <= b * subblocksPerBlock) {
InputSolutionFound(AutolykosSolution(genPk(sk), genPk(x), nonce, d))
} else {
loop(i + 1)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ergoplatform.mining

import org.ergoplatform.{OrderingBlockHeeaderFound, ProveBlockResult}
import org.ergoplatform.modifiers.history.header.Header
import scorex.crypto.authds.ADDigest
import scorex.crypto.hash.Digest32
Expand All @@ -25,14 +26,14 @@ class DefaultFakePowScheme(k: Int, n: Int) extends AutolykosPowScheme(k, n) {
votes: Array[Byte],
sk: PrivateKey,
minNonce: Long = Long.MinValue,
maxNonce: Long = Long.MaxValue): Option[Header] = {
maxNonce: Long = Long.MaxValue): ProveBlockResult = {
val (parentId, height) = AutolykosPowScheme.derivedHeaderFields(parentOpt)
val pk: EcPointType = genPk(sk)
val w: EcPointType = genPk(Random.nextLong())
val n: Array[Byte] = Array.fill(8)(0: Byte)
val d: BigInt = q / (height + 10)
val s = AutolykosSolution(pk, w, n, d)
Some(Header(version, parentId, adProofsRoot, stateRoot, transactionsRoot, timestamp,
OrderingBlockHeeaderFound(Header(version, parentId, adProofsRoot, stateRoot, transactionsRoot, timestamp,
nBits, height, extensionHash, s, votes, Array.emptyByteArray))
}

Expand Down
19 changes: 19 additions & 0 deletions ergo-core/src/main/scala/org/ergoplatform/mining/mining.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
package org.ergoplatform

import org.bouncycastle.util.BigIntegers
import org.ergoplatform.mining.AutolykosSolution
import org.ergoplatform.modifiers.ErgoFullBlock
import org.ergoplatform.modifiers.history.header.Header
import scorex.crypto.hash.Blake2b256
import sigma.crypto.{BcDlogGroup, CryptoConstants, EcPointType}
import sigma.serialization.{GroupElementSerializer, SigmaSerializer}
import sigmastate.crypto.DLogProtocol.DLogProverInput

sealed trait ProveBlockResult

case object NothingFound extends ProveBlockResult

case class OrderingBlockFound(fb: ErgoFullBlock) extends ProveBlockResult

case class OrderingBlockHeeaderFound(h: Header) extends ProveBlockResult

case class InputBlockFound(fb: ErgoFullBlock) extends ProveBlockResult

case class InputBlockHeaderFound(h: Header) extends ProveBlockResult

case class InputSolutionFound(as: AutolykosSolution) extends ProveBlockResult

case class OrderingSolutionFound(as: AutolykosSolution) extends ProveBlockResult

package object mining {

type PrivateKey = BigInt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.scalacheck.Gen
import scorex.crypto.hash.Blake2b256
import scorex.util.encode.Base16
import cats.syntax.either._
import org.ergoplatform.{InputSolutionFound, OrderingSolutionFound}

class AutolykosPowSchemeSpec extends ErgoCorePropertyTest {
import org.ergoplatform.utils.ErgoCoreTestConstants._
Expand All @@ -26,18 +27,24 @@ class AutolykosPowSchemeSpec extends ErgoCorePropertyTest {
val b = pow.getB(h.nBits)
val hbs = Ints.toByteArray(h.height)
val N = pow.calcN(h)
val newHeader = pow.checkNonces(ver, hbs, msg, sk, x, b, N, 0, 1000)
.map(s => h.copy(powSolution = s)).get
pow.validate(newHeader) shouldBe 'success

if(ver > Header.InitialVersion) {
// We remove last byte of "msg", perform PoW and check that it fails validation
require(HeaderSerializer.bytesWithoutPow(h).last == 0)
val msg2 = Blake2b256(HeaderSerializer.bytesWithoutPow(h).dropRight(1))

val newHeader2 = pow.checkNonces(ver, hbs, msg2, sk, x, b, N, 0, 1000)
.map(s => h.copy(powSolution = s)).get
pow.validate(newHeader2) shouldBe 'failure
pow.checkNonces(ver, hbs, msg, sk, x, b, N, 0, 1000) match {
case OrderingSolutionFound(as) =>
val nh = h.copy(powSolution = as)
pow.validate(nh) shouldBe 'success

if (ver > Header.InitialVersion) {
// We remove last byte of "msg", perform PoW and check that it fails validation
require(HeaderSerializer.bytesWithoutPow(h).last == 0)
val msg2 = Blake2b256(HeaderSerializer.bytesWithoutPow(h).dropRight(1))

pow.checkNonces(ver, hbs, msg2, sk, x, b, N, 0, 1000) match {
case OrderingSolutionFound(as2) =>
val nh2 = h.copy(powSolution = as2)
pow.validate(nh2) shouldBe 'failure
case _ =>
}
}
case _ =>
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ object CandidateGenerator extends ScorexLogging {

val stateContext = state.stateContext

// Calculate required difficulty for the new block
// Calculate required difficulty for the new block, the same diff for subblock
val nBits: Long = if(continueSubblock) {
lastSubblockOpt.get.subBlock.nBits // .get is ok as lastSubblockOpt.exists in continueSubblock checks emptiness
} else {
Expand Down
9 changes: 7 additions & 2 deletions src/main/scala/org/ergoplatform/mining/ErgoMiningThread.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.ergoplatform.mining

import akka.actor.{Actor, ActorRef, ActorRefFactory, Props}
import akka.pattern.StatusReply
import org.ergoplatform.{InputBlockFound, NothingFound, OrderingBlockFound}
import org.ergoplatform.mining.CandidateGenerator.{Candidate, GenerateCandidate}
import org.ergoplatform.settings.ErgoSettings
import scorex.util.ScorexLogging
Expand Down Expand Up @@ -67,13 +68,17 @@ class ErgoMiningThread(
case MineCmd =>
val lastNonceToCheck = nonce + NonceStep
powScheme.proveCandidate(candidateBlock, sk, nonce, lastNonceToCheck) match {
case Some(newBlock) =>
case OrderingBlockFound(newBlock) =>
log.info(s"Found solution, sending it for validation")
candidateGenerator ! newBlock.header.powSolution
case None =>
case InputBlockFound(_) =>
// todo: process
case NothingFound =>
log.info(s"Trying nonce $lastNonceToCheck")
context.become(mining(lastNonceToCheck, candidateBlock, solvedBlocksCount))
self ! MineCmd
case _ =>
//todo : rework ProveBlockResult hierarchy to avoid this branch
}
case GetSolvedBlocksCount =>
sender() ! SolvedBlocksCount(solvedBlocksCount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@ trait SubBlocksProcessor extends ScorexLogging {
val subBlockTransactions = mutable.Map[ModifierId, Seq[ErgoTransaction]]()

def resetState() = {

_bestSubblock = None

// todo: subBlockRecords & subBlockTransactions should be cleared a bit later, as other peers may still ask for them
subBlockRecords.clear()
subBlockTransactions.clear()
}

// sub-blocks related logic
def applySubBlockHeader(sbi: SubBlockInfo): Unit = {
if (sbi.subBlock.height > _bestSubblock.map(_.subBlock.height).getOrElse(-1)) {
resetState()
}

subBlockRecords.put(sbi.subBlock.id, sbi)

// todo: currently only one chain of subblocks considered,
// in fact there could be multiple trees here (one subblocks tree per header)
// todo: in fact there could be multiple trees here (one subblocks tree per header)
_bestSubblock match {
case None => _bestSubblock = Some(sbi)
case Some(maybeParent) if (sbi.prevSubBlockId.map(bytesToId).contains(maybeParent.subBlock.id)) =>
Expand Down

0 comments on commit e0feb83

Please sign in to comment.