Skip to content

Commit

Permalink
Merge pull request #2169 from ergoplatform/v5.0.23
Browse files Browse the repository at this point in the history
Candidate for 5.0.23 release
  • Loading branch information
kushti authored Oct 10, 2024
2 parents 0850e07 + 88e0aed commit 01d87a8
Show file tree
Hide file tree
Showing 123 changed files with 806 additions and 518 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ val circeVersion = "0.13.0"
val akkaVersion = "2.6.10"
val akkaHttpVersion = "10.2.4"

val sigmaStateVersion = "5.0.13"
val sigmaStateVersion = "5.0.14"
val ficusVersion = "1.4.7"

// for testing current sigmastate build (see sigmastate-ergo-it jenkins job)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,16 @@ import scorex.crypto.authds.merkle.MerkleProof
import scorex.crypto.authds.{LeafData, Side}
import scorex.crypto.hash.Digest
import scorex.util.encode.Base16
import sigmastate.Values.SigmaBoolean
import sigmastate._
import sigmastate.crypto.CryptoConstants.EcPointType
import sigmastate.crypto.DLogProtocol.{DLogProverInput, FirstDLogProverMessage, ProveDlog}
import sigmastate.crypto.DLogProtocol.{DLogProverInput, FirstDLogProverMessage}
import sigmastate.crypto.VerifierMessage.Challenge
import sigmastate.crypto._
import sigmastate.interpreter._
import sigmastate.serialization.OpCodes
import sigma.serialization.{OpCodes, SigSerializer}
import org.ergoplatform.sdk.JsonCodecs
import sigmastate.eval.Extensions.ArrayOps
import sigmastate.utils.Helpers._
import sigma.Extensions.ArrayOps
import sigma.crypto._
import sigma.data._

import java.math.BigInteger
import scala.annotation.nowarn
Expand Down Expand Up @@ -129,7 +128,7 @@ trait ApiCodecs extends JsonCodecs {
})

implicit val secretBigIntEncoder: Encoder[BigInteger] = Encoder.instance { w =>
ErgoAlgos.encode(BigIntegers.asUnsignedByteArray(CryptoConstants.groupSize, w)).asJson
ErgoAlgos.encode(BigIntegers.asUnsignedByteArray(sigma.crypto.groupSize, w)).asJson
}

implicit val secretBigIntDecoder: Decoder[BigInteger] = arrayBytesDecoder.map { bytes =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import org.ergoplatform.nodeView.mempool.TransactionMembershipProof
import scorex.crypto.authds.{ADDigest, SerializedAdProof}
import scorex.crypto.hash.{Blake2b256, Digest32}
import scorex.util.{ModifierId, ScorexLogging}
import sigmastate.crypto.DLogProtocol.ProveDlog
import sigmastate.crypto.CryptoFacade
import sigma.crypto.CryptoFacade
import sigma.data.ProveDlog

import scala.annotation.tailrec
import scala.util.Try
Expand All @@ -27,6 +27,10 @@ import scala.util.Try
* Based on k-sum problem, so general idea is to find k numbers in a table of size N, such that
* sum of numbers (or a hash of the sum) is less than target value.
*
* There are two version of Autolykos PoW scheme, Autolykos v1 and v2. The main difference is that
* Autolykos v1 is (weakly) non-outsourceable, while v2 is outsourceable and also eliminates some vectors of
* optimizations a miner could follow.
*
* See https://docs.ergoplatform.com/ErgoPow.pdf for details
*
* CPU Mining process is implemented in inefficient way and should not be used in real environment.
Expand Down Expand Up @@ -58,7 +62,8 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
val IncreasePeriodForN: Height = 50 * 1024

/**
* On this height, the table (`N` value) will stop to grow
* On this height, the table (`N` value) will stop to grow.
* Max N on and after this height would be 2,143,944,600 which is still less than 2^^31.
*/
val NIncreasementHeightMax: Height = 4198400

Expand Down Expand Up @@ -97,25 +102,37 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
* Checks that `header` contains correct solution of the Autolykos PoW puzzle.
*/
def validate(header: Header): Try[Unit] = Try {
val b = getB(header.nBits)
if (header.version == 1) {
// for version 1, we check equality of left and right sides of the equation
require(checkPoWForVersion1(header, b), "Incorrect points")
require(checkPoWForVersion1(header), "Incorrect points")
} else {
// for version 2, we're calculating hit and compare it with target
val hit = hitForVersion2(header)
require(hit < b, "h(f) < b condition not met")
require(checkPoWForVersion2(header), "h(f) < b condition not met")
}
}

/**
* Check PoW for Autolykos v2 header
*
* @param header - header to check PoW for
* @return whether PoW is valid or not
*/
def checkPoWForVersion2(header: Header): Boolean = {
val b = getB(header.nBits)
// for version 2, we're calculating hit and compare it with target
val hit = hitForVersion2(header)
hit < b
}

/**
* Check PoW for Autolykos v1 header
*
* @param header - header to check PoW for
* @param b - PoW target
* @return whether PoW is valid or not
*/
def checkPoWForVersion1(header: Header, b: BigInt): Boolean = {
def checkPoWForVersion1(header: Header): Boolean = {

val b = getB(header.nBits) // PoW target

val version = 1: Byte

val msg = msgByHeader(header)
Expand Down Expand Up @@ -152,7 +169,6 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
* @return PoW hit
*/
def hitForVersion2(header: Header): BigInt = {
val version = 2: Byte

val msg = msgByHeader(header)
val nonce = header.powSolution.n
Expand All @@ -161,6 +177,25 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {

val N = calcN(header)

hitForVersion2ForMessage(msg, nonce, h, N)
}

/**
* Get a PoW hit for custom message (not necessarily a block header) with Autolykos v2.
*
* PoW then can can be checked as hit < b, where b is PoW target value
*
* @param msg - message to check PoW on
* @param nonce - PoW nonce
* @param h - for Ergo blockchain, this is height encoded as bytes. For other use-cases, could be
* unique value on each call or constant (in the latter case more pre-computations
* could be possible
* @param N - table size
* @return pow hit
*/
def hitForVersion2ForMessage(msg: Array[Byte], nonce: Array[Byte], h: Array[Byte], N: Int): BigInt = {
val version = 2: Byte // autolykos protocol version, used in genElement only

val prei8 = BigIntegers.fromUnsignedByteArray(hash(Bytes.concat(msg, nonce)).takeRight(8))
val i = BigIntegers.asUnsignedByteArray(4, prei8.mod(BigInt(N).underlying()))
val f = Blake2b256(Bytes.concat(i, h, M)).drop(1) // .drop(1) is the same as takeRight(31)
Expand Down Expand Up @@ -244,7 +279,9 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
//Proving-related code which is not critical for consensus below

/**
* Find a nonce from `minNonce` to `maxNonce`, such that header with the specified fields will contain
* Autolykos solver suitable for CPU-mining in testnet and devnets.
*
* Finds a nonce from `minNonce` to `maxNonce`, such that header with the specified fields will contain
* correct solution of the Autolykos PoW puzzle.
*/
def prove(parentOpt: Option[Header],
Expand All @@ -262,7 +299,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging {
val (parentId, height) = AutolykosPowScheme.derivedHeaderFields(parentOpt)

val h = HeaderWithoutPow(version, parentId, adProofsRoot, stateRoot, transactionsRoot, timestamp,
nBits, height, extensionHash, votes)
nBits, height, extensionHash, votes, Array.emptyByteArray)
val msg = msgByHeader(h)
val b = getB(nBits)
val x = randomSecret()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import org.ergoplatform.modifiers.history.header.Header.Version
import org.ergoplatform.settings.Algos
import org.ergoplatform.serialization.ErgoSerializer
import scorex.util.serialization.{Reader, Writer}
import sigmastate.crypto.CryptoConstants
import sigmastate.crypto.CryptoConstants.EcPointType
import sigma.crypto.{CryptoConstants, EcPointType}

/**
* Solution for an Autolykos PoW puzzle.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ object CandidateUtils {
candidate.nBits,
height,
extensionRoot,
candidate.votes
candidate.votes,
Array.emptyByteArray
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.ergoplatform.mining
import org.ergoplatform.modifiers.history.header.Header
import scorex.crypto.authds.ADDigest
import scorex.crypto.hash.Digest32
import sigmastate.crypto.CryptoConstants.EcPointType
import sigma.crypto.EcPointType

import scala.util.{Random, Success, Try}

Expand Down Expand Up @@ -33,7 +33,7 @@ class DefaultFakePowScheme(k: Int, n: Int) extends AutolykosPowScheme(k, n) {
val d: BigInt = q / (height + 10)
val s = AutolykosSolution(pk, w, n, d)
Some(Header(version, parentId, adProofsRoot, stateRoot, transactionsRoot, timestamp,
nBits, height, extensionHash, s, votes))
nBits, height, extensionHash, s, votes, Array.emptyByteArray))
}

override def realDifficulty(header: Header): PrivateKey = header.requiredDifficulty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import scala.annotation.tailrec
* in range from 0 to a maximum number divisible by q without remainder.
* If yes, it returns the result mod q, otherwise make one more iteration using hash as an input.
* This is done to ensure uniform distribution of the resulting numbers.
*
* Used in Autolykos v1 only!
*/
class NumericHash(val q: BigInt) extends ScorexLogging with ScorexEncoding {
class ModQHash(val q: BigInt) extends ScorexLogging with ScorexEncoding {
assert(q.bigInteger.bitLength() <= 256, "We use 256 bit hash here")
// biggest number <= 2^256 that is divisible by q without remainder
val validRange: BigInt = (BigInt(2).pow(256) / q) * q
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.circe.syntax._
import io.circe.{Encoder, Json}
import org.ergoplatform.http.api.ApiCodecs
import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height
import sigmastate.crypto.DLogProtocol.ProveDlog
import sigma.data.ProveDlog


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object DifficultySerializer extends ErgoSerializer[NBits] {
if (size >= 1) bytes(4) = ((compact >> 16) & 0xFF).toByte
if (size >= 2) bytes(5) = ((compact >> 8) & 0xFF).toByte
if (size >= 3) bytes(6) = (compact & 0xFF).toByte
decodeMPI(bytes, hasLength = true)
decodeMPI(bytes)
}

/**
Expand Down Expand Up @@ -78,19 +78,13 @@ object DifficultySerializer extends ErgoSerializer[NBits] {
* MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of
* a 4 byte big endian length field, followed by the stated number of bytes representing
* the number in big endian format (with a sign bit).
*
* @param hasLength can be set to false if the given array is missing the 4 byte length field
*/
@SuppressWarnings(Array("NullAssignment"))
private def decodeMPI(mpi: Array[Byte], hasLength: Boolean): BigInteger = {
var buf: Array[Byte] = null // scalastyle:ignore
if (hasLength) {
val length: Int = readUint32BE(mpi).toInt
buf = new Array[Byte](length)
System.arraycopy(mpi, 4, buf, 0, length)
} else {
buf = mpi
}
private def decodeMPI(mpi: Array[Byte]): BigInteger = {

val length: Int = readUint32BE(mpi).toInt
val buf = new Array[Byte](length)
System.arraycopy(mpi, 4, buf, 0, length)

if (buf.length == 0) {
BigInteger.ZERO
} else {
Expand Down
9 changes: 4 additions & 5 deletions ergo-core/src/main/scala/org/ergoplatform/mining/mining.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ package org.ergoplatform

import org.bouncycastle.util.BigIntegers
import scorex.crypto.hash.Blake2b256
import sigmastate.crypto.CryptoConstants.EcPointType
import sigmastate.crypto.{BcDlogGroup, CryptoConstants}
import sigma.crypto.{BcDlogGroup, CryptoConstants, EcPointType}
import sigma.serialization.{GroupElementSerializer, SigmaSerializer}
import sigmastate.crypto.DLogProtocol.DLogProverInput
import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer}

package object mining {

Expand All @@ -19,14 +18,14 @@ package object mining {
// and also to obtain target in both Autolykos v1 and v2
val q: BigInt = group.order

private val hashFn: NumericHash = new NumericHash(q)
private val modQHashFn: ModQHash = new ModQHash(q)

/**
* Hash function which output is in Zq. Used in Autolykos v.1
* @param in - input (bit-string)
* @return - output(in Zq)
*/
def hashModQ(in: Array[Byte]): BigInt = hashFn.hash(in)
def hashModQ(in: Array[Byte]): BigInt = modQHashFn.hash(in)

/**
* Convert byte array to unsigned integer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import org.ergoplatform.modifiers.history.header.Header._
import org.ergoplatform.nodeView.history.ErgoHistoryUtils._
import org.ergoplatform.settings.Constants
import scorex.util._
import sigmastate.crypto.CryptoConstants.EcPointType
import sigmastate.eval.CGroupElement
import sigmastate.eval.Extensions._
import sigma.Extensions.ArrayOps
import sigma.crypto.EcPointType
import sigma.data.CGroupElement

/**
* Only header fields that can be predicted by a miner
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.ergoplatform.modifiers.history.header

import cats.syntax.either._
import cats.syntax.either._ // needed for Scala 2.11
import sigmastate.utils.Helpers._
import io.circe.syntax._
import io.circe.{Decoder, Encoder, HCursor}
Expand All @@ -17,9 +17,10 @@ import org.ergoplatform.serialization.ErgoSerializer
import scorex.crypto.authds.ADDigest
import scorex.crypto.hash.Digest32
import scorex.util._
import sigmastate.crypto.CryptoConstants.EcPointType
import sigmastate.eval.Extensions._
import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader}
import sigma.Extensions.ArrayOps
import sigma.crypto.EcPointType
import sigma.data.{CAvlTree, CBigInt, CGroupElement}
import sigmastate.eval.CHeader

import scala.annotation.nowarn
import scala.concurrent.duration.FiniteDuration
Expand All @@ -40,6 +41,7 @@ import scala.concurrent.duration.FiniteDuration
* @param extensionRoot - Merkle tree digest of the extension section of the block
* @param powSolution - solution for the proof-of-work puzzle
* @param votes - votes for changing system parameters
* @param unparsedBytes - bytes of fields added in future versions of the protocol and not parseable
* @param sizeOpt - optionally, size of the header (to avoid serialization on calling .length)
*/
case class Header(override val version: Header.Version,
Expand All @@ -53,8 +55,9 @@ case class Header(override val version: Header.Version,
override val extensionRoot: Digest32,
powSolution: AutolykosSolution,
override val votes: Array[Byte], //3 bytes
override val unparsedBytes: Array[Byte],
override val sizeOpt: Option[Int] = None) extends HeaderWithoutPow(version, parentId, ADProofsRoot, stateRoot, transactionsRoot, timestamp,
nBits, height, extensionRoot, votes) with PreHeader with BlockSection {
nBits, height, extensionRoot, votes, unparsedBytes) with PreHeader with BlockSection {

override def serializedId: Array[Header.Version] = Algos.hash(bytes)

Expand Down Expand Up @@ -138,6 +141,11 @@ object Header extends ApiCodecs {
*/
val Interpreter50Version: Byte = 3

/**
* Block version after the 6.0 soft-fork
* 6.0 interpreter (EIP-50)
*/
val Interpreter60Version: Byte = 4

def toSigma(header: Header): sigma.Header =
CHeader(
Expand Down Expand Up @@ -180,7 +188,8 @@ object Header extends ApiCodecs {
"size" -> h.size.asJson,
"extensionId" -> Algos.encode(h.extensionId).asJson,
"transactionsId" -> Algos.encode(h.transactionsId).asJson,
"adProofsId" -> Algos.encode(h.ADProofsId).asJson
"adProofsId" -> Algos.encode(h.ADProofsId).asJson,
"unparsedBytes" -> Algos.encode(h.unparsedBytes).asJson
).asJson
}

Expand All @@ -197,8 +206,10 @@ object Header extends ApiCodecs {
version <- c.downField("version").as[Byte]
votes <- c.downField("votes").as[String]
solutions <- c.downField("powSolutions").as[AutolykosSolution]
unparsedBytes <- c.downField("unparsedBytes").as[Option[Array[Byte]]]
} yield Header(version, parentId, adProofsRoot, stateRoot,
transactionsRoot, timestamp, nBits, height, extensionHash, solutions, Algos.decode(votes).get)
transactionsRoot, timestamp, nBits, height, extensionHash, solutions, Algos.decode(votes).get,
unparsedBytes.getOrElse(Array.emptyByteArray))
}

}
Loading

0 comments on commit 01d87a8

Please sign in to comment.