Add cache generation and FNV hashing functions
This commit is contained in:
parent
bdff346e03
commit
a557f8e675
|
@ -1,11 +1,14 @@
|
||||||
# Copyright (c) 2018 Status Research & Development GmbH
|
# 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).
|
# 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.
|
# Note: array/seq indexing requires an Ordinal, uint64 are not.
|
||||||
# So to index arrays/seq we would need to cast uint64 to int anyway ...
|
# 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):
|
dm.rem == 0 and dm.quot.isPrime):
|
||||||
result -= 2 * MIX_BYTES
|
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:
|
when isMainModule:
|
||||||
echo get_full_size(100000)
|
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])
|
|
@ -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)
|
|
@ -4,7 +4,7 @@
|
||||||
# Primality testing. TODO: a scalable implementation (i.e. Miller-Rabin)
|
# Primality testing. TODO: a scalable implementation (i.e. Miller-Rabin)
|
||||||
# See https://github.com/mratsim/nim-projecteuler/blob/master/src/lib/primes.nim
|
# 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.}=
|
proc isPrime*(x: SomeUnsignedInt): bool {.noSideEffect.}=
|
||||||
|
|
Loading…
Reference in New Issue