-
Notifications
You must be signed in to change notification settings - Fork 41
[6.0.0] Header.checkPow method #968
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
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
d6e1bec
merging w. 6.0.0
kushti 361c7a4
merging with i969, removing BouncyCastle dep from Autolykos2PowValida…
kushti 15de2e2
merging i969
kushti 17f5eb6
CHeader from i969
kushti fd8d6eb
checkPow cost
kushti 928d8cb
LangSpec.md update
kushti 81f285b
mrging w.6.0.0
kushti 76f1a90
merging w. 969
kushti 01ede1c
MethodCallSerializerSpecification fix
kushti 40d5e54
relaxed check in check pow test before activation
kushti b677b18
merging w. i969
kushti e501a8b
polishing, LSV6 test
kushti 89f2df6
checkPoW added to collectMethods
kushti ba878b8
fixing GraphIRReflection for checkPow, test for isoHeader for known v…
kushti b41d16c
test vectors for Blake2b256 and Long -> BigInt
kushti c2788b4
fixing checkPow test in JS
kushti 17c3e6b
mrging w. 6.0.0
kushti 2e3db09
Merge branch 'v6.0.0' of github.com:ScorexFoundation/sigmastate-inter…
kushti e7fde10
versioned execution depending on tree version, newFeature test
kushti 04eb6aa
Merge branch 'i969' of github.com:ScorexFoundation/sigmastate-interpr…
kushti ea0890b
tree versioning removed, scaladoc
kushti File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
|
|
||
| yarn.lock | ||
| *.log | ||
| yarn.lock | ||
| docs/spec/out/ | ||
| test-out/ | ||
| flamegraphs/ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| package sigma.util | ||
|
|
||
| import java.math.BigInteger | ||
|
|
||
| object NBitsUtils { | ||
|
|
||
| /** | ||
| * <p>The "compact" format is a representation of a whole number N using an unsigned 32 bit number similar to a | ||
| * floating point format. The most significant 8 bits are the unsigned exponent of base 256. This exponent can | ||
| * be thought of as "number of bytes of N". The lower 23 bits are the mantissa. Bit number 24 (0x800000) represents | ||
| * the sign of N. Therefore, N = (-1^sign) * mantissa * 256^(exponent-3).</p> | ||
| * | ||
| * <p>Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). MPI uses the most significant bit of the | ||
| * first byte as sign. Thus 0x1234560000 is compact 0x05123456 and 0xc0de000000 is compact 0x0600c0de. Compact | ||
| * 0x05c0de00 would be -0x40de000000.</p> | ||
| * | ||
| * <p>Bitcoin only uses this "compact" format for encoding difficulty targets, which are unsigned 256bit quantities. | ||
| * Thus, all the complexities of the sign bit and using base 256 are probably an implementation accident.</p> | ||
| */ | ||
| def decodeCompactBits(compact: Long): BigInt = { | ||
| val size: Int = (compact >> 24).toInt & 0xFF | ||
| val bytes: Array[Byte] = new Array[Byte](4 + size) | ||
| bytes(3) = size.toByte | ||
| 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) | ||
| } | ||
|
|
||
| /** | ||
| * @see Utils#decodeCompactBits(long) | ||
| */ | ||
| def encodeCompactBits(requiredDifficulty: BigInt): Long = { | ||
| val value = requiredDifficulty.bigInteger | ||
| var result: Long = 0L | ||
| var size: Int = value.toByteArray.length | ||
| if (size <= 3) { | ||
| result = value.longValue << 8 * (3 - size) | ||
| } else { | ||
| result = value.shiftRight(8 * (size - 3)).longValue | ||
| } | ||
| // The 0x00800000 bit denotes the sign. | ||
| // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. | ||
| if ((result & 0x00800000L) != 0) { | ||
| result >>= 8 | ||
| size += 1 | ||
| } | ||
| result |= size << 24 | ||
| val a: Int = if (value.signum == -1) 0x00800000 else 0 | ||
| result |= a | ||
| result | ||
| } | ||
|
|
||
|
|
||
| /** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in big endian format. */ | ||
| def readUint32BE(bytes: Array[Byte]): Long = ((bytes(0) & 0xffL) << 24) | ((bytes(1) & 0xffL) << 16) | ((bytes(2) & 0xffL) << 8) | (bytes(3) & 0xffL) | ||
|
|
||
| /** | ||
| * 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). | ||
| * | ||
| */ | ||
| 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 { | ||
| val isNegative: Boolean = (buf(0) & 0x80) == 0x80 | ||
| if (isNegative) buf(0) = (buf(0) & 0x7f).toByte | ||
| val result: BigInteger = new BigInteger(buf) | ||
| if (isNegative) { | ||
| result.negate | ||
| } else { | ||
| result | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
176 changes: 176 additions & 0 deletions
176
data/shared/src/main/scala/sigma/pow/Autolykos2PowValidation.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| package sigma.pow | ||
|
|
||
|
|
||
| import scorex.crypto.hash.Blake2b256 | ||
| import scorex.utils.{Bytes, Ints, Longs} | ||
| import sigma.Header | ||
| import sigma.crypto.{BcDlogGroup, BigIntegers, CryptoConstants} | ||
| import sigma.util.NBitsUtils | ||
|
|
||
| /** | ||
| * Functions used to validate Autolykos2 Proof-of-Work. | ||
| */ | ||
| object Autolykos2PowValidation { | ||
|
|
||
| type Height = Int | ||
|
|
||
| /** | ||
| * k value for k-sum problem Autolykos is based on (find k numbers in table on N size) | ||
| */ | ||
| private val k = 32 | ||
|
|
||
| /** | ||
| * Initial size of N value for k-sum problem Autolykos is based on (find k numbers in table on N size). | ||
| * It grows from it since predefined block height in Autolykos 2. | ||
| */ | ||
| private val NStart = 26 | ||
|
|
||
| /** | ||
| * Group order, used in Autolykos V.1 for non-outsourceability, | ||
| * and also to obtain target in both Autolykos v1 and v2 | ||
| */ | ||
| private val q: BigInt = CryptoConstants.dlogGroup.order | ||
|
|
||
| /** | ||
| * Number of elements in a table to find k-sum problem solution on top of | ||
| */ | ||
| val NBase: Int = Math.pow(2, NStart.toDouble).toInt | ||
|
|
||
| /** | ||
| * Initial height since which table (`N` value) starting to increase by 5% per `IncreasePeriodForN` blocks | ||
| */ | ||
| val IncreaseStart: Height = 600 * 1024 | ||
|
|
||
| /** | ||
| * Table size (`N`) increased every 50 * 1024 blocks | ||
| */ | ||
| val IncreasePeriodForN: Height = 50 * 1024 | ||
|
|
||
| /** | ||
| * 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 | ||
|
|
||
| /** | ||
| * Blake2b256 hash function invocation | ||
| * @param in - input bit-string | ||
| * @return - 256 bits (32 bytes) array | ||
| */ | ||
| def hash(in: Array[Byte]): Array[Byte] = Blake2b256.hash(in) | ||
|
|
||
| /** | ||
| * Convert byte array to unsigned integer | ||
| * @param in - byte array | ||
| * @return - unsigned integer | ||
| */ | ||
| def toBigInt(in: Array[Byte]): BigInt = BigInt(BigIntegers.fromUnsignedByteArray(in)) | ||
|
|
||
| /** | ||
| * Constant data to be added to hash function to increase its calculation time | ||
| */ | ||
| val M: Array[Byte] = (0 until 1024).toArray.flatMap(i => Longs.toByteArray(i.toLong)) | ||
|
|
||
| /** | ||
| * Calculates table size (N value) for a given height (moment of time) | ||
| * | ||
| * @see papers/yellow/pow/ErgoPow.tex for full description and test vectors | ||
| * @param headerHeight - height of a header to mine | ||
| * @return - N value | ||
| */ | ||
| def calcN(headerHeight: Height): Int = { | ||
| val height = Math.min(NIncreasementHeightMax, headerHeight) | ||
| if (height < IncreaseStart) { | ||
| NBase | ||
| } else { | ||
| val itersNumber = (height - IncreaseStart) / IncreasePeriodForN + 1 | ||
| (1 to itersNumber).foldLeft(NBase) { case (step, _) => | ||
| step / 100 * 105 | ||
| } | ||
| } | ||
| } | ||
|
|
||
| def calcN(header: Header): Int = calcN(header.height) | ||
|
|
||
| /** | ||
| * Hash function that takes `m` and `nonceBytes` and returns a list of size `k` with numbers in | ||
| * [0,`N`) | ||
| */ | ||
| private def genIndexes(k: Int, seed: Array[Byte], N: Int): Seq[Int] = { | ||
| val hash = Blake2b256(seed) | ||
| val extendedHash = Bytes.concat(hash, hash.take(3)) | ||
| (0 until k).map { i => | ||
| BigInt(1, extendedHash.slice(i, i + 4)).mod(N).toInt | ||
| } | ||
| }.ensuring(_.length == k) | ||
|
|
||
| /** | ||
| * Generate element of Autolykos equation. | ||
| */ | ||
| private def genElementV2(indexBytes: Array[Byte], heightBytes: => Array[Byte]): BigInt = { | ||
| // Autolykos v. 2: H(j|h|M) (line 5 from the Algo 2 of the spec) | ||
| toBigInt(hash(Bytes.concat(indexBytes, heightBytes, M)).drop(1)) | ||
| } | ||
|
|
||
| def hitForVersion2ForMessage(k: Int, msg: Array[Byte], nonce: Array[Byte], h: Array[Byte], N: Int): BigInt = { | ||
|
|
||
| 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) | ||
| val seed = Bytes.concat(f, msg, nonce) // Autolykos v1, Alg. 2, line4: | ||
|
|
||
| val indexes = genIndexes(k, seed, N) | ||
| //pk and w not used in v2 | ||
| val elems = indexes.map(idx => genElementV2(Ints.toByteArray(idx), h)) | ||
| val f2 = elems.sum | ||
|
|
||
| // sum as byte array is always about 32 bytes | ||
| val array: Array[Byte] = BigIntegers.asUnsignedByteArray(32, f2.underlying()) | ||
| val ha = hash(array) | ||
| toBigInt(ha) | ||
| } | ||
|
|
||
| /** | ||
| * Header digest ("message" for default GPU miners) a miner is working on | ||
| */ | ||
| def msgByHeader(h: Header): Array[Byte] = Blake2b256(h.serializeWithoutPoW.toArray) | ||
|
|
||
| /** | ||
| * Get hit for Autolykos v2 header (to test it then against PoW target) | ||
| * | ||
| * @param header - header to check PoW for | ||
| * @return PoW hit | ||
| */ | ||
| def hitForVersion2(header: Header): BigInt = { | ||
|
|
||
| val msg = msgByHeader(header) | ||
| val nonce = header.powNonce | ||
|
|
||
| val h = Ints.toByteArray(header.height) // used in AL v.2 only | ||
|
|
||
| val N = calcN(header) | ||
|
|
||
| hitForVersion2ForMessage(k, msg, nonce.toArray, h, N) | ||
| } | ||
|
|
||
| /** | ||
| * Get target `b` from encoded difficulty `nBits` | ||
| */ | ||
| def getB(nBits: Long): BigInt = { | ||
| q / NBitsUtils.decodeCompactBits(nBits) | ||
| } | ||
|
|
||
| /** | ||
| * 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 | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.