Merge pull request #17 from status-im/Rit

use non-EOL macOS in CI; apply styleCheck:error; prefer func to proc(){.noSideEffect.}; check refc in Nim 2.0+
This commit is contained in:
tersec 2024-02-20 23:44:29 +00:00 committed by GitHub
commit 953b8ed994
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 105 additions and 108 deletions

View File

@ -24,7 +24,7 @@ jobs:
builder: ubuntu-20.04 builder: ubuntu-20.04
- target: - target:
os: macos os: macos
builder: macos-11 builder: macos-12
- target: - target:
os: windows os: windows
builder: windows-latest builder: windows-latest

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
import math, endians, import math, endians,
@ -28,7 +28,7 @@ const
# ############################################################################### # ###############################################################################
# Parameters # Parameters
proc get_cache_size*(block_number: uint): uint {.noSideEffect.}= func get_cache_size*(block_number: uint): uint =
result = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number div EPOCH_LENGTH) result = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number div EPOCH_LENGTH)
result -= HASH_BYTES result -= HASH_BYTES
while (let dm = divmod(result, HASH_BYTES); while (let dm = divmod(result, HASH_BYTES);
@ -37,7 +37,7 @@ proc get_cache_size*(block_number: uint): uint {.noSideEffect.}=
# Means checking that reminder == 0 and quotient is prime # Means checking that reminder == 0 and quotient is prime
result -= 2 * HASH_BYTES result -= 2 * HASH_BYTES
proc get_data_size*(block_number: uint): uint {.noSideEffect.}= func get_data_size*(block_number: uint): uint =
result = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number div EPOCH_LENGTH) result = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number div EPOCH_LENGTH)
result -= MIX_BYTES result -= MIX_BYTES
while (let dm = divmod(result, MIX_BYTES); while (let dm = divmod(result, MIX_BYTES);
@ -47,16 +47,16 @@ proc get_data_size*(block_number: uint): uint {.noSideEffect.}=
# ############################################################################### # ###############################################################################
# Fetch from lookup tables of 2048 epochs of data sizes and cache sizes # Fetch from lookup tables of 2048 epochs of data sizes and cache sizes
proc get_datasize_lut*(block_number: Natural): uint64 {.noSideEffect, inline.} = func get_datasize_lut*(block_number: Natural): uint64 {.inline.} =
data_sizes[block_number div EPOCH_LENGTH] data_sizes[block_number div EPOCH_LENGTH]
proc get_cachesize_lut*(block_number: Natural): uint64 {.noSideEffect, inline.} = func get_cachesize_lut*(block_number: Natural): uint64 {.inline.} =
cache_sizes[block_number div EPOCH_LENGTH] cache_sizes[block_number div EPOCH_LENGTH]
# ############################################################################### # ###############################################################################
# Cache generation # Cache generation
proc mkcache*(cache_size: uint64, seed: Hash[256]): seq[Hash[512]] {.noSideEffect.}= func mkcache*(cache_size: uint64, seed: Hash[256]): seq[Hash[512]] =
# Cache size # Cache size
let n = int(cache_size div HASH_BYTES) let n = int(cache_size div HASH_BYTES)
@ -82,7 +82,7 @@ proc mkcache*(cache_size: uint64, seed: Hash[256]): seq[Hash[512]] {.noSideEffec
const FNV_PRIME = 0x01000193 const FNV_PRIME = 0x01000193
proc fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): uint32 {.inline, noSideEffect.}= func fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): uint32 {.inline.}=
# Original formula is ((v1 * FNV_PRIME) xor v2) mod 2^32 # Original formula is ((v1 * FNV_PRIME) xor v2) mod 2^32
# However contrary to Python and depending on the type T, # However contrary to Python and depending on the type T,
@ -103,7 +103,7 @@ proc fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): uint32 {.inline, noSideEffe
# ############################################################################### # ###############################################################################
# Full dataset calculation # Full dataset calculation
proc calc_dataset_item*(cache: seq[Hash[512]], i: Natural): Hash[512] {.noSideEffect, noInit.} = func calc_dataset_item*(cache: seq[Hash[512]], i: Natural): Hash[512] {.noinit.} =
let n = cache.len let n = cache.len
const r: uint32 = HASH_BYTES div WORD_BYTES const r: uint32 = HASH_BYTES div WORD_BYTES
@ -159,14 +159,14 @@ template hashimoto(header: Hash[256],
assert MIX_BYTES mod HASH_BYTES == 0 assert MIX_BYTES mod HASH_BYTES == 0
# combine header+nonce into a 64 byte seed # combine header+nonce into a 64 byte seed
var s{.noInit.}: Hash[512] var s{.noinit.}: Hash[512]
let s_bytes = cast[ptr array[64, byte]](addr s) # Alias for to interpret s as a byte array let s_bytes = cast[ptr array[64, byte]](addr s) # Alias for to interpret s as a byte array
let s_words = cast[ptr array[16, uint32]](addr s) # Alias for to interpret s as an uint32 array let s_words = cast[ptr array[16, uint32]](addr s) # Alias for to interpret s as an uint32 array
s_bytes[][0..<32] = header.data # We first populate the first 40 bytes of s with the concatenation s_bytes[][0..<32] = header.data # We first populate the first 40 bytes of s with the concatenation
# In template we need to dereference first otherwise it's not considered as var # In template we need to dereference first otherwise it's not considered as var
var nonceLE{.noInit.}: array[8, byte] # the nonce should be concatenated with its LITTLE ENDIAN representation var nonceLE{.noinit.}: array[8, byte] # the nonce should be concatenated with its LITTLE ENDIAN representation
littleEndian64(addr nonceLE, unsafeAddr nonce) littleEndian64(addr nonceLE, unsafeAddr nonce)
s_bytes[][32..<40] = cast[array[8,byte]](nonceLE) s_bytes[][32..<40] = cast[array[8,byte]](nonceLE)
@ -174,7 +174,7 @@ template hashimoto(header: Hash[256],
# start the mix with replicated s # start the mix with replicated s
assert MIX_BYTES div HASH_BYTES == 2 assert MIX_BYTES div HASH_BYTES == 2
var mix{.noInit.}: array[32, uint32] var mix{.noinit.}: array[32, uint32]
mix[0..<16] = s_words[] mix[0..<16] = s_words[]
mix[16..<32] = s_words[] mix[16..<32] = s_words[]
@ -184,7 +184,7 @@ template hashimoto(header: Hash[256],
let p1{.inject.} = p + 1 let p1{.inject.} = p + 1
# Unrolled: for j in range(MIX_BYTES / HASH_BYTES): => for j in 0 ..< 2 # Unrolled: for j in range(MIX_BYTES / HASH_BYTES): => for j in 0 ..< 2
var newdata{.noInit.}: type mix var newdata{.noinit.}: type mix
newdata[0..<16] = cast[array[16, uint32]](dataset_lookup_p) newdata[0..<16] = cast[array[16, uint32]](dataset_lookup_p)
newdata[16..<32] = cast[array[16, uint32]](dataset_lookup_p1) newdata[16..<32] = cast[array[16, uint32]](dataset_lookup_p1)
@ -198,13 +198,13 @@ template hashimoto(header: Hash[256],
for i in countup(0, mix.len - 1, 4): for i in countup(0, mix.len - 1, 4):
cmix[i div 4] = mix[i].fnv(mix[i+1]).fnv(mix[i+2]).fnv(mix[i+3]) cmix[i div 4] = mix[i].fnv(mix[i+1]).fnv(mix[i+2]).fnv(mix[i+3])
var concat{.noInit.}: array[64 + 32, byte] var concat{.noinit.}: array[64 + 32, byte]
concat[0..<64] = s_bytes[] concat[0..<64] = s_bytes[]
concat[64..<96] = cast[array[32, byte]](result.mix_digest) concat[64..<96] = cast[array[32, byte]](result.mix_digest)
result.value = keccak_256(concat) result.value = keccak_256(concat)
proc hashimoto_light*(full_size:Natural, cache: seq[Hash[512]], func hashimoto_light*(full_size:Natural, cache: seq[Hash[512]],
header: Hash[256], nonce: uint64): HashimotoHash {.noSideEffect.} = header: Hash[256], nonce: uint64): HashimotoHash =
hashimoto(header, hashimoto(header,
nonce, nonce,
@ -213,8 +213,8 @@ proc hashimoto_light*(full_size:Natural, cache: seq[Hash[512]],
calc_data_set_item(cache, p1), calc_data_set_item(cache, p1),
result) result)
proc hashimoto_full*(full_size:Natural, dataset: seq[Hash[512]], func hashimoto_full*(full_size:Natural, dataset: seq[Hash[512]],
header: Hash[256], nonce: uint64): HashimotoHash {.noSideEffect.} = header: Hash[256], nonce: uint64): HashimotoHash =
# TODO spec mentions full_size but I don't think we need it (retrieve it from dataset.len) # TODO spec mentions full_size but I don't think we need it (retrieve it from dataset.len)
hashimoto(header, hashimoto(header,
nonce, nonce,
@ -225,6 +225,6 @@ proc hashimoto_full*(full_size:Natural, dataset: seq[Hash[512]],
# ############################################################################### # ###############################################################################
# Defining the seed hash # Defining the seed hash
proc get_seedhash*(block_number: uint64): Hash[256] {.noSideEffect.} = func get_seedhash*(block_number: uint64): Hash[256] =
for i in 0 ..< int(block_number div EPOCH_LENGTH): for i in 0 ..< int(block_number div EPOCH_LENGTH):
result = keccak256 result.data result = keccak256 result.data

View File

@ -1,3 +1,5 @@
mode = ScriptMode.Verbose
packageName = "ethash" packageName = "ethash"
version = "0.0.1" version = "0.0.1"
author = "Status Research & Development GmbH" author = "Status Research & Development GmbH"
@ -7,25 +9,20 @@ srcDir = "src"
### Dependencies ### Dependencies
requires "nim >= 0.18.0", "nimcrypto >= 0.1.0" requires "nim >= 1.6.0", "nimcrypto >= 0.1.0"
proc test(name: string, lang: string = "c") = proc test(name: string, args: string) =
if not dirExists "build": if not dirExists "build":
mkDir "build" mkDir "build"
--run exec "nim c --styleCheck:usages --styleCheck:error --outdir:./build/ " & args & " --run tests/" & name & ".nim"
switch("out", ("./build/" & name)) if (NimMajor, NimMinor) > (1, 6):
setCommand lang, "tests/" & name & ".nim" exec "nim c --styleCheck:usages --styleCheck:error --mm:refc --outdir:./build/ " & args & " --run tests/" & name & ".nim"
task test, "Run Proof-of-Work tests (without mining)": task test, "Run Proof-of-Work tests (without mining)":
test "all_tests" test "all_tests", ""
task testRelease, "test release mode": task testRelease, "test release mode":
switch("define", "release") test "all_tests", "-d:release"
testTask()
task test_mining, "Run Proof-of-Work and mining tests (test in release mode + OpenMP + march=native)": task test_mining, "Run Proof-of-Work and mining tests (test in release mode + OpenMP + march=native)":
switch("define", "release") test "all_tests", "-d:release -d:openmp -d:march_native -d:ethash_mining"
switch("define", "openmp")
switch("define", "march_native")
switch("define", "ethash_mining")
test "all_tests"

View File

@ -6,7 +6,7 @@ import ./internal
############################################### ###############################################
proc toHex*[N: static[int]](ba: array[N, byte]): string {.noSideEffect.}= func toHex*[N: static[int]](ba: array[N, byte]): string =
## Convert a big-endian byte array to its hex representation ## Convert a big-endian byte array to its hex representation
## Output is in lowercase ## Output is in lowercase
## ##
@ -19,7 +19,7 @@ proc toHex*[N: static[int]](ba: array[N, byte]): string {.noSideEffect.}=
result[2*i+1] = hexChars[int ba[i] and 0xF] result[2*i+1] = hexChars[int ba[i] and 0xF]
proc readHexChar(c: char): byte {.noSideEffect.}= func readHexChar(c: char): byte =
## Converts an hex char to a byte ## Converts an hex char to a byte
case c case c
of '0'..'9': result = byte(ord(c) - ord('0')) of '0'..'9': result = byte(ord(c) - ord('0'))
@ -28,7 +28,7 @@ proc readHexChar(c: char): byte {.noSideEffect.}=
else: else:
raise newException(ValueError, $c & "is not a hexademical character") raise newException(ValueError, $c & "is not a hexademical character")
proc hexToByteArrayBE*[N: static[int]](hexStr: string): array[N, byte] {.noSideEffect, noInit.}= func hexToByteArrayBE*[N: static[int]](hexStr: string): array[N, byte] {.noinit.}=
## Read an hex string and store it in a Byte Array in Big-Endian order ## Read an hex string and store it in a Byte Array in Big-Endian order
var i = 0 var i = 0
if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'): if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'):

View File

@ -1,12 +1,12 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
when defined(openmp): when defined(openmp):
{.passC: "-fopenmp".} {.passc: "-fopenmp".}
{.passL: "-fopenmp".} {.passl: "-fopenmp".}
when defined(march_native): when defined(march_native):
{.passC: "-march=native".} {.passc: "-march=native".}
import ./proof_of_work import ./proof_of_work
export proof_of_work export proof_of_work

View File

@ -60,11 +60,11 @@ proc mulCarry(a, b: uint64): tuple[carry, unit: uint64] =
result.unit += z0 result.unit += z0
result.carry = (result.unit < z0).uint64 + z2 + z1 shr 32 result.carry = (result.unit < z0).uint64 + z2 + z1 shr 32
proc isValid(nonce: uint64, func isValid(nonce: uint64,
difficulty: uint64, difficulty: uint64,
full_size: Natural, full_size: Natural,
dataset: seq[MDigest[512]], dataset: seq[MDigest[512]],
header: MDigest[256]): bool {.noSideEffect.}= header: MDigest[256]): bool =
# Boundary is 2^256/difficulty # Boundary is 2^256/difficulty
# A valid nonce will have: hashimoto < 2^256/difficulty # A valid nonce will have: hashimoto < 2^256/difficulty
# We can't represent 2^256 as an uint256 so as a workaround we use: # We can't represent 2^256 as an uint256 so as a workaround we use:

View File

@ -1,13 +1,13 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
import nimcrypto import nimcrypto
proc as_u32_words*[bits: static[int]](x: MDigest[bits]): array[bits div 32, uint32] {.inline, noSideEffect, noInit.}= func as_u32_words*[bits: static[int]](x: MDigest[bits]): array[bits div 32, uint32] {.inline, noinit.}=
# Convert an hash to its uint32 representation # Convert an hash to its uint32 representation
cast[type result](x) cast[type result](x)
proc readHexChar(c: char): byte {.noSideEffect.}= func readHexChar(c: char): byte =
## Converts an hex char to a byte ## Converts an hex char to a byte
case c case c
of '0'..'9': result = byte(ord(c) - ord('0')) of '0'..'9': result = byte(ord(c) - ord('0'))
@ -16,7 +16,7 @@ proc readHexChar(c: char): byte {.noSideEffect.}=
else: else:
raise newException(ValueError, $c & "is not a hexademical character") raise newException(ValueError, $c & "is not a hexademical character")
proc hexToByteArrayBE*[N: static[int]](hexStr: string): array[N, byte] {.noSideEffect, noInit.}= func hexToByteArrayBE*[N: static[int]](hexStr: string): array[N, byte] {.noinit.}=
## Read an hex string and store it in a Byte Array in Big-Endian order ## Read an hex string and store it in a Byte Array in Big-Endian order
var i = 0 var i = 0
if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'): if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'):
@ -28,7 +28,7 @@ proc hexToByteArrayBE*[N: static[int]](hexStr: string): array[N, byte] {.noSideE
result[i] = hexStr[2*i].readHexChar shl 4 or hexStr[2*i+1].readHexChar result[i] = hexStr[2*i].readHexChar shl 4 or hexStr[2*i+1].readHexChar
inc(i) inc(i)
proc hexToSeqBytesBE*(hexStr: string): seq[byte] {.noSideEffect.}= func hexToSeqBytesBE*(hexStr: string): seq[byte] =
## Read an hex string and store it in a sequence of bytes in Big-Endian order ## Read an hex string and store it in a sequence of bytes in Big-Endian order
var i = 0 var i = 0
if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'): if hexStr[i] == '0' and (hexStr[i+1] == 'x' or hexStr[i+1] == 'X'):
@ -41,7 +41,7 @@ proc hexToSeqBytesBE*(hexStr: string): seq[byte] {.noSideEffect.}=
result[i] = hexStr[2*i].readHexChar shl 4 or hexStr[2*i+1].readHexChar result[i] = hexStr[2*i].readHexChar shl 4 or hexStr[2*i+1].readHexChar
inc(i) inc(i)
proc toHex*[N: static[int]](ba: array[N, byte]): string {.noSideEffect.}= func toHex*[N: static[int]](ba: array[N, byte]): string =
## Convert a big-endian byte array to its hex representation ## Convert a big-endian byte array to its hex representation
## Output is in lowercase ## Output is in lowercase
@ -52,7 +52,7 @@ proc toHex*[N: static[int]](ba: array[N, byte]): string {.noSideEffect.}=
result[2*i] = hexChars[int ba[i] shr 4 and 0xF] result[2*i] = hexChars[int ba[i] shr 4 and 0xF]
result[2*i+1] = hexChars[int ba[i] and 0xF] result[2*i+1] = hexChars[int ba[i] and 0xF]
proc toHex*(ba: seq[byte]): string {.noSideEffect, noInit.}= func toHex*(ba: seq[byte]): string {.noinit.}=
## Convert a big-endian byte sequence to its hex representation ## Convert a big-endian byte sequence to its hex representation
## Output is in lowercase ## Output is in lowercase
@ -65,7 +65,7 @@ proc toHex*(ba: seq[byte]): string {.noSideEffect, noInit.}=
result[2*i] = hexChars[int ba[i] shr 4 and 0xF] result[2*i] = hexChars[int ba[i] shr 4 and 0xF]
result[2*i+1] = hexChars[int ba[i] and 0xF] result[2*i+1] = hexChars[int ba[i] and 0xF]
proc toByteArrayBE*[T: SomeInteger](num: T): array[T.sizeof, byte] {.noSideEffect, noInit, inline.}= func toByteArrayBE*[T: SomeInteger](num: T): array[T.sizeof, byte] {.noinit, inline.}=
## Convert an int (in native host endianness) to a big-endian byte array ## Convert an int (in native host endianness) to a big-endian byte array
# Note: only works on devel # Note: only works on devel
@ -78,5 +78,5 @@ proc toByteArrayBE*[T: SomeInteger](num: T): array[T.sizeof, byte] {.noSideEffec
for i in 0 ..< N: for i in 0 ..< N:
result[i] = byte(num shr T((N-1-i) * 8)) result[i] = byte(num shr T((N-1-i) * 8))
proc toByteArrayBE*[bits: static[int]](x: MDigest[bits]): array[bits div 8, byte] {.inline, noSideEffect, noInit.}= func toByteArrayBE*[bits: static[int]](x: MDigest[bits]): array[bits div 8, byte] {.inline, noinit.}=
cast[type result](x.data) cast[type result](x.data)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
@ -27,7 +27,7 @@ template zipMap*[N: static[int], T, U](
)) ))
{.pragma: align64, codegenDecl: "$# $# __attribute__((aligned(64)))".} {.pragma: align64, codegenDecl: "$# $# __attribute__((aligned(64)))".}
var result{.noInit, align64.}: array[N, outType] var result{.noinit, align64.}: array[N, outType]
for i, x {.inject.}, y {.inject.} in enumerateZip(a, b): for i, x {.inject.}, y {.inject.} in enumerateZip(a, b):
{.unroll: 4.} # This is a no-op at the moment {.unroll: 4.} # This is a no-op at the moment

View File

@ -1,6 +1,6 @@
# From https://github.com/numforge/number-theory/ # From https://github.com/numforge/number-theory/
# MIT Licence # MIT Licence
# Copyright (c) 2016 Mamy Ratsimbazafy # Copyright (c) 2016-2024 Mamy Ratsimbazafy
# ########### Number of bits to represent a number # ########### Number of bits to represent a number
@ -59,16 +59,16 @@ type
proc ldiv(a, b: clong): ldiv_t {.importc: "ldiv", header: "<stdlib.h>".} proc ldiv(a, b: clong): ldiv_t {.importc: "ldiv", header: "<stdlib.h>".}
proc lldiv(a, b: clonglong): lldiv_t {.importc: "lldiv", header: "<stdlib.h>".} proc lldiv(a, b: clonglong): lldiv_t {.importc: "lldiv", header: "<stdlib.h>".}
proc divmod*(a, b: int32): tuple[quot, rem: clong] {.inline, noSideEffect, noInit.}= func divmod*(a, b: int32): tuple[quot, rem: clong] {.inline, noinit.}=
## Compute quotient and reminder of integer division in a single intrinsics operation ## Compute quotient and reminder of integer division in a single intrinsics operation
# TODO: changing clong to int32 poses an issue for some reason # TODO: changing clong to int32 poses an issue for some reason
cast[type result](ldiv(a,b)) cast[type result](ldiv(a,b))
proc divmod*(a, b: int64): tuple[quot, rem: int64] {.inline, noSideEffect, noInit.}= func divmod*(a, b: int64): tuple[quot, rem: int64] {.inline, noinit.}=
## Compute quotient and reminder of integer division in a single intrinsicsoperation ## Compute quotient and reminder of integer division in a single intrinsicsoperation
cast[type result](lldiv(a,b)) cast[type result](lldiv(a,b))
proc divmod*[T: SomeUnsignedInt](a, b: T): tuple[quot, rem: T] {.inline, noSideEffect, noInit.}= func divmod*[T: SomeUnsignedInt](a, b: T): tuple[quot, rem: T] {.inline, noinit.}=
# There is no single instruction for unsigned ints # There is no single instruction for unsigned ints
# Hopefully the compiler does its work properly # Hopefully the compiler does its work properly
(a div b, a mod b) (a div b, a mod b)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
# Primality testing. TODO: a scalable implementation (i.e. Miller-Rabin) # Primality testing. TODO: a scalable implementation (i.e. Miller-Rabin)
@ -6,14 +6,14 @@
import ./intmath import ./intmath
proc isPrime*[T: SomeUnsignedInt](x: T): bool {.noSideEffect.}= func isPrime*[T: SomeUnsignedInt](x: T): bool =
for i in 2.T .. isqrt x: for i in 2.T .. isqrt x:
if x mod i == 0: if x mod i == 0:
return false return false
return true return true
proc isPrime*(x: Natural): bool {.noSideEffect.}= func isPrime*(x: Natural): bool =
for i in 2 .. isqrt x: for i in 2 .. isqrt x:
if x mod i == 0: if x mod i == 0:
return false return false
return true return true

View File

@ -28,7 +28,7 @@ const
# ############################################################################### # ###############################################################################
# Parameters # Parameters
proc get_cache_size*(block_number: uint64): uint64 {.noSideEffect.}= func get_cache_size*(block_number: uint64): uint64 =
result = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number div EPOCH_LENGTH) result = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number div EPOCH_LENGTH)
result -= HASH_BYTES result -= HASH_BYTES
while (let dm = intmath.divmod(result, HASH_BYTES); while (let dm = intmath.divmod(result, HASH_BYTES);
@ -37,7 +37,7 @@ proc get_cache_size*(block_number: uint64): uint64 {.noSideEffect.}=
# means checking that remainder == 0 and quotient is prime # means checking that remainder == 0 and quotient is prime
result -= 2 * HASH_BYTES result -= 2 * HASH_BYTES
proc get_data_size*(block_number: uint64): uint64 {.noSideEffect.}= func get_data_size*(block_number: uint64): uint64 =
result = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number div EPOCH_LENGTH) result = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number div EPOCH_LENGTH)
result -= MIX_BYTES result -= MIX_BYTES
while (let dm = intmath.divmod(result, MIX_BYTES); while (let dm = intmath.divmod(result, MIX_BYTES);
@ -48,16 +48,16 @@ proc get_data_size*(block_number: uint64): uint64 {.noSideEffect.}=
# Fetch from lookup tables of 2048 epochs of data sizes and cache sizes # Fetch from lookup tables of 2048 epochs of data sizes and cache sizes
import ./data_sizes import ./data_sizes
proc get_datasize_lut*(block_number: Natural): uint64 {.noSideEffect, inline.} = func get_datasize_lut*(block_number: Natural): uint64 {.inline.} =
data_sizes[block_number div EPOCH_LENGTH] data_sizes[block_number div EPOCH_LENGTH]
proc get_cachesize_lut*(block_number: Natural): uint64 {.noSideEffect, inline.} = func get_cachesize_lut*(block_number: Natural): uint64 {.inline.} =
cache_sizes[block_number div EPOCH_LENGTH] cache_sizes[block_number div EPOCH_LENGTH]
# ############################################################################### # ###############################################################################
# Cache generation # Cache generation
proc mkcache*(cache_size: uint64, seed: MDigest[256]): seq[MDigest[512]] {.noSideEffect.}= func mkcache*(cache_size: uint64, seed: MDigest[256]): seq[MDigest[512]] =
# Cache size # Cache size
let n = int(cache_size div HASH_BYTES) let n = int(cache_size div HASH_BYTES)
@ -83,7 +83,7 @@ proc mkcache*(cache_size: uint64, seed: MDigest[256]): seq[MDigest[512]] {.noSid
const FNV_PRIME = 0x01000193 const FNV_PRIME = 0x01000193
proc fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): uint32 {.inline, noSideEffect.}= func fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): uint32 {.inline.}=
# Original formula is ((v1 * FNV_PRIME) xor v2) mod 2^32 # Original formula is ((v1 * FNV_PRIME) xor v2) mod 2^32
# However contrary to Python and depending on the type T, # However contrary to Python and depending on the type T,
@ -104,7 +104,7 @@ proc fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): uint32 {.inline, noSideEffe
# ############################################################################### # ###############################################################################
# Full dataset calculation # Full dataset calculation
proc calc_dataset_item*(cache: seq[MDigest[512]], i: Natural): MDigest[512] {.noSideEffect, noInit.} = func calc_dataset_item*(cache: seq[MDigest[512]], i: Natural): MDigest[512] {.noinit.} =
let n = cache.len let n = cache.len
const r: uint32 = HASH_BYTES div WORD_BYTES const r: uint32 = HASH_BYTES div WORD_BYTES
@ -161,14 +161,14 @@ template hashimoto(header: MDigest[256],
# combine header+nonce into a 64 byte seed # combine header+nonce into a 64 byte seed
{.pragma: align64, codegenDecl: "$# $# __attribute__((aligned(64)))".} {.pragma: align64, codegenDecl: "$# $# __attribute__((aligned(64)))".}
var s{.align64, noInit.}: MDigest[512] var s{.align64, noinit.}: MDigest[512]
let s_bytes = cast[ptr array[64, byte]](addr s) # Alias to interpret s as a byte array let s_bytes = cast[ptr array[64, byte]](addr s) # Alias to interpret s as a byte array
let s_words = cast[ptr array[16, uint32]](addr s) # Alias to interpret s as an uint32 array let s_words = cast[ptr array[16, uint32]](addr s) # Alias to interpret s as an uint32 array
s_bytes[][0..<32] = header.data # We first populate the first 40 bytes of s with the concatenation s_bytes[][0..<32] = header.data # We first populate the first 40 bytes of s with the concatenation
# In template we need to dereference first otherwise it's not considered as var # In template we need to dereference first otherwise it's not considered as var
var nonceLE{.noInit.}: array[8, byte] # the nonce should be concatenated with its LITTLE ENDIAN representation var nonceLE{.noinit.}: array[8, byte] # the nonce should be concatenated with its LITTLE ENDIAN representation
littleEndian64(addr nonceLE, unsafeAddr nonce) littleEndian64(addr nonceLE, unsafeAddr nonce)
s_bytes[][32..<40] = nonceLE s_bytes[][32..<40] = nonceLE
@ -176,7 +176,7 @@ template hashimoto(header: MDigest[256],
# start the mix with replicated s # start the mix with replicated s
assert MIX_BYTES div HASH_BYTES == 2 assert MIX_BYTES div HASH_BYTES == 2
var mix{.align64, noInit.}: array[32, uint32] var mix{.align64, noinit.}: array[32, uint32]
mix[0..<16] = s_words[] mix[0..<16] = s_words[]
mix[16..<32] = s_words[] mix[16..<32] = s_words[]
@ -186,7 +186,7 @@ template hashimoto(header: MDigest[256],
let p1{.inject.} = p + 1 let p1{.inject.} = p + 1
# Unrolled: for j in range(MIX_BYTES / HASH_BYTES): => for j in 0 ..< 2 # Unrolled: for j in range(MIX_BYTES / HASH_BYTES): => for j in 0 ..< 2
var newdata{.noInit.}: type mix var newdata{.noinit.}: type mix
newdata[0..<16] = cast[array[16, uint32]](dataset_lookup_p) newdata[0..<16] = cast[array[16, uint32]](dataset_lookup_p)
newdata[16..<32] = cast[array[16, uint32]](dataset_lookup_p1) newdata[16..<32] = cast[array[16, uint32]](dataset_lookup_p1)
@ -200,23 +200,23 @@ template hashimoto(header: MDigest[256],
for i in countup(0, mix.len - 1, 4): for i in countup(0, mix.len - 1, 4):
cmix[i div 4] = mix[i].fnv(mix[i+1]).fnv(mix[i+2]).fnv(mix[i+3]) cmix[i div 4] = mix[i].fnv(mix[i+1]).fnv(mix[i+2]).fnv(mix[i+3])
var concat{.noInit.}: array[64 + 32, byte] var concat{.noinit.}: array[64 + 32, byte]
concat[0..<64] = s_bytes[] concat[0..<64] = s_bytes[]
concat[64..<96] = cast[array[32, byte]](result.mix_digest) concat[64..<96] = cast[array[32, byte]](result.mix_digest)
result.value = keccak_256.digest concat result.value = keccak_256.digest concat
proc hashimoto_light*(full_size:Natural, cache: seq[MDigest[512]], func hashimoto_light*(full_size:Natural, cache: seq[MDigest[512]],
header: MDigest[256], nonce: uint64): HashimotoHash {.noSideEffect.} = header: MDigest[256], nonce: uint64): HashimotoHash =
hashimoto(header, hashimoto(header,
nonce, nonce,
full_size, full_size,
calc_data_set_item(cache, p), calc_dataset_item(cache, p),
calc_data_set_item(cache, p1), calc_dataset_item(cache, p1),
result) result)
proc hashimoto_full*(full_size:Natural, dataset: seq[MDigest[512]], func hashimoto_full*(full_size:Natural, dataset: seq[MDigest[512]],
header: MDigest[256], nonce: uint64): HashimotoHash {.noSideEffect.} = header: MDigest[256], nonce: uint64): HashimotoHash =
# TODO spec mentions full_size but I don't think we need it (retrieve it from dataset.len) # TODO spec mentions full_size but I don't think we need it (retrieve it from dataset.len)
hashimoto(header, hashimoto(header,
nonce, nonce,
@ -227,6 +227,6 @@ proc hashimoto_full*(full_size:Natural, dataset: seq[MDigest[512]],
# ############################################################################### # ###############################################################################
# Defining the seed hash # Defining the seed hash
proc get_seedhash*(block_number: uint64): MDigest[256] {.noSideEffect.} = func get_seedhash*(block_number: uint64): MDigest[256] =
for i in 0 ..< int(block_number div EPOCH_LENGTH): for i in 0 ..< int(block_number div EPOCH_LENGTH):
result = keccak256.digest result.data result = keccak256.digest result.data

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
import ../src/ethash, unittest, times, strutils, nimcrypto import ../src/ethash, unittest, times, strutils, nimcrypto
@ -11,12 +11,12 @@ suite "Test mining":
# POC-9 testnet, epoch 0 # POC-9 testnet, epoch 0
let let
blck = 22'u # block number blck = 22'u # block number
cache = mkcache(get_cachesize(blck), get_seedhash(blck)) cache = mkcache(get_cache_size(blck), get_seedhash(blck))
header = cast[MDigest[256]]( header = cast[MDigest[256]](
hexToByteArrayBE[32]("372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d") hexToByteArrayBE[32]("372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d")
) )
difficulty = 132416'u64 difficulty = 132416'u64
full_size = get_datasize(blck) full_size = get_data_size(blck)
echo "\nGenerating dataset" echo "\nGenerating dataset"
var start = epochTime() var start = epochTime()

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2024 Status Research & Development GmbH
# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0).
import ../src/ethash, unittest, strutils, algorithm, random, sequtils, nimcrypto import ../src/ethash, unittest, strutils, algorithm, random, sequtils, nimcrypto
@ -43,8 +43,8 @@ suite "Endianness (not implemented)":
suite "Genesis parameters": suite "Genesis parameters":
# https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/test/c/test.cpp#L155-L180 # https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/test/c/test.cpp#L155-L180
let let
full_size = get_datasize(0) full_size = get_data_size(0)
cache_size = get_cachesize(0) cache_size = get_cache_size(0)
test "Full dataset size should be less or equal DATASET_BYTES_INIT": test "Full dataset size should be less or equal DATASET_BYTES_INIT":
check: full_size <= DATASET_BYTES_INIT check: full_size <= DATASET_BYTES_INIT
@ -79,19 +79,19 @@ suite "Epoch change":
check: get_cache_size(EPOCH_LENGTH * 2048 - 1) == 285081536'u check: get_cache_size(EPOCH_LENGTH * 2048 - 1) == 285081536'u
test "Full dataset size at the change of epochs - Look-up tables": test "Full dataset size at the change of epochs - Look-up tables":
check: get_data_size_lut(EPOCH_LENGTH - 1) == 1073739904'u check: get_datasize_lut(EPOCH_LENGTH - 1) == 1073739904'u
check: get_data_size_lut(EPOCH_LENGTH) == 1082130304'u check: get_datasize_lut(EPOCH_LENGTH) == 1082130304'u
check: get_data_size_lut(EPOCH_LENGTH + 1) == 1082130304'u check: get_datasize_lut(EPOCH_LENGTH + 1) == 1082130304'u
check: get_data_size_lut(EPOCH_LENGTH * 2046) == 18236833408'u check: get_datasize_lut(EPOCH_LENGTH * 2046) == 18236833408'u
check: get_data_size_lut(EPOCH_LENGTH * 2047) == 18245220736'u check: get_datasize_lut(EPOCH_LENGTH * 2047) == 18245220736'u
test "Cache size at the change of epochs - Look-up tables": test "Cache size at the change of epochs - Look-up tables":
check: get_cache_size_lut(EPOCH_LENGTH - 1) == 16776896'u check: get_cachesize_lut(EPOCH_LENGTH - 1) == 16776896'u
check: get_cache_size_lut(EPOCH_LENGTH) == 16907456'u check: get_cachesize_lut(EPOCH_LENGTH) == 16907456'u
check: get_cache_size_lut(EPOCH_LENGTH + 1) == 16907456'u check: get_cachesize_lut(EPOCH_LENGTH + 1) == 16907456'u
check: get_cache_size_lut(EPOCH_LENGTH * 2046) == 284950208'u check: get_cachesize_lut(EPOCH_LENGTH * 2046) == 284950208'u
check: get_cache_size_lut(EPOCH_LENGTH * 2047) == 285081536'u check: get_cachesize_lut(EPOCH_LENGTH * 2047) == 285081536'u
check: get_cache_size_lut(EPOCH_LENGTH * 2048 - 1) == 285081536'u check: get_cachesize_lut(EPOCH_LENGTH * 2048 - 1) == 285081536'u
test "Random testing of full size": test "Random testing of full size":
# https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/test/python/test_pyethash.py#L23-L28 # https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/test/python/test_pyethash.py#L23-L28
@ -191,13 +191,13 @@ suite "Real blocks test":
# https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/test/c/test.cpp#L603-L617 # https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/test/c/test.cpp#L603-L617
# POC-9 testnet, epoch 0 # POC-9 testnet, epoch 0
let blck = 22'u # block number let blck = 22'u # block number
let cache = mkcache(get_cachesize(blck), get_seedhash(blck)) let cache = mkcache(get_cache_size(blck), get_seedhash(blck))
let header = cast[MDigest[256]]( let header = cast[MDigest[256]](
hexToByteArrayBE[32]("372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d") hexToByteArrayBE[32]("372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d")
) )
let light = hashimoto_light( let light = hashimoto_light(
get_datasize(blck), get_data_size(blck),
cache, cache,
header, header,
0x495732e0ed7a801c'u 0x495732e0ed7a801c'u
@ -206,7 +206,7 @@ suite "Real blocks test":
check: light.value == cast[MDigest[256]]( check: light.value == cast[MDigest[256]](
hexToByteArrayBE[32]("00000b184f1fdd88bfd94c86c39e65db0c36144d5e43f745f722196e730cb614") hexToByteArrayBE[32]("00000b184f1fdd88bfd94c86c39e65db0c36144d5e43f745f722196e730cb614")
) )
check: light.mixDigest == cast[MDigest[256]]( check: light.mix_digest == cast[MDigest[256]](
hexToByteArrayBE[32]("2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5") hexToByteArrayBE[32]("2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5")
) )
@ -214,19 +214,19 @@ suite "Real blocks test":
# https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/ethash_test.go#L63-L69 # https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/ethash_test.go#L63-L69
# POC-9 testnet, epoch 1 # POC-9 testnet, epoch 1
let blck = 30001'u # block number let blck = 30001'u # block number
let cache = mkcache(get_cachesize(blck), get_seedhash(blck)) let cache = mkcache(get_cache_size(blck), get_seedhash(blck))
let header = cast[MDigest[256]]( let header = cast[MDigest[256]](
hexToByteArrayBE[32]("7e44356ee3441623bc72a683fd3708fdf75e971bbe294f33e539eedad4b92b34") hexToByteArrayBE[32]("7e44356ee3441623bc72a683fd3708fdf75e971bbe294f33e539eedad4b92b34")
) )
let light = hashimoto_light( let light = hashimoto_light(
get_datasize(blck), get_data_size(blck),
cache, cache,
header, header,
0x318df1c8adef7e5e'u 0x318df1c8adef7e5e'u
) )
check: light.mixDigest == cast[MDigest[256]]( check: light.mix_digest == cast[MDigest[256]](
hexToByteArrayBE[32]("144b180aad09ae3c81fb07be92c8e6351b5646dda80e6844ae1b697e55ddde84") hexToByteArrayBE[32]("144b180aad09ae3c81fb07be92c8e6351b5646dda80e6844ae1b697e55ddde84")
) )
@ -234,18 +234,18 @@ suite "Real blocks test":
# https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/ethash_test.go#L70-L78 # https://github.com/ethereum/ethash/blob/f5f0a8b1962544d2b6f40df8e4b0d9a32faf8f8e/ethash_test.go#L70-L78
# POC-9 testnet, epoch 2 # POC-9 testnet, epoch 2
let blck = 60000'u # block number let blck = 60000'u # block number
let cache = mkcache(get_cachesize(blck), get_seedhash(blck)) let cache = mkcache(get_cache_size(blck), get_seedhash(blck))
let header = cast[MDigest[256]]( let header = cast[MDigest[256]](
hexToByteArrayBE[32]("5fc898f16035bf5ac9c6d9077ae1e3d5fc1ecc3c9fd5bee8bb00e810fdacbaa0") hexToByteArrayBE[32]("5fc898f16035bf5ac9c6d9077ae1e3d5fc1ecc3c9fd5bee8bb00e810fdacbaa0")
) )
let light = hashimoto_light( let light = hashimoto_light(
get_datasize(blck), get_data_size(blck),
cache, cache,
header, header,
0x50377003e5d830ca'u 0x50377003e5d830ca'u
) )
check: light.mixDigest == cast[MDigest[256]]( check: light.mix_digest == cast[MDigest[256]](
hexToByteArrayBE[32]("ab546a5b73c452ae86dadd36f0ed83a6745226717d3798832d1b20b489e82063") hexToByteArrayBE[32]("ab546a5b73c452ae86dadd36f0ed83a6745226717d3798832d1b20b489e82063")
) )