diff --git a/src/ethash.nim b/src/ethash.nim index 11975b8..5889e49 100644 --- a/src/ethash.nim +++ b/src/ethash.nim @@ -1,11 +1,14 @@ # Copyright (c) 2018 Status Research & Development GmbH # Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). -import math, number_theory # https://github.com/numforge/number-theory +import math, sequtils, + number_theory, # Not on nimble yet: https://github.com/numforge/number-theory + keccak_tiny, + zero_functional -import ./private/primes +import ./private/[primes, bytes] -# TODO: consider switching from default int to uint64 +# TODO: Switching from default int to uint64 # Note: array/seq indexing requires an Ordinal, uint64 are not. # So to index arrays/seq we would need to cast uint64 to int anyway ... @@ -45,6 +48,61 @@ proc get_full_size(block_number: Natural): int {.noSideEffect.}= dm.rem == 0 and dm.quot.isPrime): result -= 2 * MIX_BYTES +# ############################################################################### +# Cache generation +proc mkcache(cache_size, seed: int): seq[Hash[512]] {.noSideEffect.}= + + let n = cache_size div HASH_BYTES + + # Sequentially produce the initial dataset + result = newSeq[Hash[512]](n) + result[0] = sha3_512 seed.asByteArray # TODO: spec is unclear if we interpret integers as array of bytes + + for i in 1 ..< n: + result[i] = sha3_512 result[i-1].asByteArray + + # Use a low-round version of randmemohash + for _ in 0 ..< CACHE_ROUNDS: + for i in 0 ..< n: + let + v = asByteArray(result[i])[0].int mod n + a = result[(i-1+n) mod n].asByteArray + b = result[v].asByteArray + result[i] = sha3_512 zip(a, b)-->map(it[0] xor it[1]) + +# ############################################################################### +# Data aggregation function + +const FNV_PRIME = 0x01000193 + +proc fnv[T: SomeUnsignedInt or Natural](v1, v2: T): T = + + # Original formula is ((v1 * FNV_PRIME) xor v2) mod 2^32 + # However contrary to Python and depending on the type T, + # in Nim (v1 * FNV_PRIME) can overflow + # We can't do 2^32 with an int (only 2^32-1) + # and in general (a xor b) mod c != (a mod c) xor (b mod c) + # + # Thankfully + # We know that: + # - (a xor b) and c == (a and c) xor (b and c) + # - for powers of 2: a mod 2^p == a and (2^p - 1) + # - 2^32 - 1 == high(uint32) + + const mask: T = 2^32 - 1 + + mulmod(v1 and mask, FNV_PRIME.T, (2^32).T) xor (v2 and mask) + + +# ############################################################################### when isMainModule: - echo get_full_size(100000) \ No newline at end of file + echo get_full_size(100000) + let a = sha3_512 1234.asByteArray + + echo a + + + echo zip([0, 1, 2, 3], [10, 20, 30, 40]) --> map(it[0] * it[1]) # [0, 20, 60, 120] + + echo zip(a.asByteArray, a.asByteArray) --> map(it[0] xor it[1]) \ No newline at end of file diff --git a/src/private/bytes.nim b/src/private/bytes.nim new file mode 100644 index 0000000..de7437b --- /dev/null +++ b/src/private/bytes.nim @@ -0,0 +1,13 @@ +# Copyright (c) 2018 Status Research & Development GmbH +# Distributed under the Apache v2 License (license terms are at http://www.apache.org/licenses/LICENSE-2.0). + +import keccak_tiny + +proc asByteArray*[T: not (ref|ptr|string)](data: T): array[sizeof(T), byte] = + ## Cast stack allocated types to an array of byte + cast[type result](data) + +proc asByteArray*(data: Hash[512]): array[64, byte] = + ## Workaround: Nim cannot evaluate size of arrays + ## https://github.com/nim-lang/Nim/issues/5802 + cast[type result](data) \ No newline at end of file diff --git a/src/private/primes.nim b/src/private/primes.nim index 364465c..02d68cb 100644 --- a/src/private/primes.nim +++ b/src/private/primes.nim @@ -4,7 +4,7 @@ # Primality testing. TODO: a scalable implementation (i.e. Miller-Rabin) # See https://github.com/mratsim/nim-projecteuler/blob/master/src/lib/primes.nim -import number_theory # https://github.com/numforge/number-theory +import number_theory # Not on Nimble yet: https://github.com/numforge/number-theory proc isPrime*(x: SomeUnsignedInt): bool {.noSideEffect.}=