mirror of
https://github.com/status-im/nim-ethash.git
synced 2025-02-19 15:34:21 +00:00
Add basic hashing tests and use keccak instead of sha3
This commit is contained in:
parent
d830f6993f
commit
388fd963f0
@ -1,4 +1,4 @@
|
||||
packageName = "eth_keys"
|
||||
packageName = "ethash"
|
||||
version = "0.0.1"
|
||||
author = "Status Research & Development GmbH"
|
||||
description = "A reimplementation in pure Nim of ethash, the ethereum proof-of-work algorithm"
|
||||
@ -7,9 +7,9 @@ srcDir = "src"
|
||||
|
||||
### Dependencies
|
||||
|
||||
requires "nim >= 0.17.2", "number_theory", "keccak_tiny >= 0.1.0"
|
||||
requires "nim >= 0.17.2", "keccak_tiny >= 0.1.0"
|
||||
|
||||
proc test(name: string, lang: string = "cpp") =
|
||||
proc test(name: string, lang: string = "c") =
|
||||
if not dirExists "build":
|
||||
mkDir "build"
|
||||
if not dirExists "nimcache":
|
||||
|
@ -58,10 +58,10 @@ proc mkcache*(cache_size: int, seed: seq[byte]): seq[Hash[512]] {.noSideEffect.}
|
||||
|
||||
# Sequentially produce the initial dataset
|
||||
result = newSeq[Hash[512]](n)
|
||||
result[0] = sha3_512 seed
|
||||
result[0] = keccak512 seed
|
||||
|
||||
for i in 1 ..< n:
|
||||
result[i] = sha3_512 result[i-1].toU512
|
||||
result[i] = keccak512 result[i-1].toU512
|
||||
|
||||
# Use a low-round version of randmemohash
|
||||
for _ in 0 ..< CACHE_ROUNDS:
|
||||
@ -70,14 +70,14 @@ proc mkcache*(cache_size: int, seed: seq[byte]): seq[Hash[512]] {.noSideEffect.}
|
||||
v = result[i].toU512[0] mod n.uint
|
||||
a = result[(i-1+n) mod n].toU512
|
||||
b = result[v.int].toU512
|
||||
result[i] = sha3_512 zipMap(a, b, x xor y)
|
||||
result[i] = keccak512 zipMap(a, b, x xor y)
|
||||
|
||||
# ###############################################################################
|
||||
# Data aggregation function
|
||||
|
||||
const FNV_PRIME = 0x01000193
|
||||
|
||||
proc fnv[T: SomeUnsignedInt or Natural](v1, v2: T): T {.inline, noSideEffect.}=
|
||||
proc fnv*[T: SomeUnsignedInt or Natural](v1, v2: T): T {.inline, noSideEffect.}=
|
||||
|
||||
# Original formula is ((v1 * FNV_PRIME) xor v2) mod 2^32
|
||||
# However contrary to Python and depending on the type T,
|
||||
@ -91,9 +91,11 @@ proc fnv[T: SomeUnsignedInt or Natural](v1, v2: T): T {.inline, noSideEffect.}=
|
||||
# - for powers of 2: a mod 2^p == a and (2^p - 1)
|
||||
# - 2^32 - 1 == high(uint32)
|
||||
|
||||
const mask: T = T(2^32) - 1
|
||||
|
||||
mulmod(v1 and mask, FNV_PRIME.T, (2^32).T) xor (v2 and mask)
|
||||
# # mulmod(v1 and mask, FNV_PRIME.T, (2^32).T) xor (v2 and mask)
|
||||
# Casting to uint32 should do the modulo and masking just fine
|
||||
|
||||
(v1.uint32 * FNV_PRIME) xor v2.uint32
|
||||
|
||||
# ###############################################################################
|
||||
# Full dataset calculation
|
||||
@ -109,7 +111,7 @@ proc calc_dataset_item(cache: seq[Hash[512]], i: Natural): Hash[512] {.noSideEff
|
||||
mix[0] = mix[0] xor i.uint64
|
||||
else:
|
||||
mix[high(mix)] = mix[high(0)] xor i.uint64
|
||||
mix[] = toU512 sha3_512 mix[]
|
||||
mix[] = toU512 keccak512 mix[]
|
||||
|
||||
# FNV with a lots of random cache nodes based on i
|
||||
# TODO: we use FNV with word size 64 bit while ethash implementation is using 32 bit words
|
||||
@ -118,7 +120,7 @@ proc calc_dataset_item(cache: seq[Hash[512]], i: Natural): Hash[512] {.noSideEff
|
||||
let cache_index = fnv(i.uint64 xor j, mix[j mod r])
|
||||
mix[] = zipMap(mix[], cache[cache_index.int mod n].toU512, fnv(x, y))
|
||||
|
||||
result = sha3_512 mix[]
|
||||
result = keccak512 mix[]
|
||||
|
||||
proc calc_dataset(cache: var seq[Hash[512]]) {.noSideEffect.} =
|
||||
for i, hash in cache.mpairs:
|
||||
@ -127,7 +129,7 @@ proc calc_dataset(cache: var seq[Hash[512]]) {.noSideEffect.} =
|
||||
# ###############################################################################
|
||||
when isMainModule:
|
||||
echo get_full_size(100000)
|
||||
let a = sha3_512 1234.toU512
|
||||
let a = keccak512 1234.toU512
|
||||
|
||||
echo a
|
||||
|
||||
|
@ -66,14 +66,22 @@ proc hexToSeqBytesBE*(hexStr: string): seq[byte] {.noSideEffect.}=
|
||||
result[i] = hexStr[2*i].readHexChar shl 4 or hexStr[2*i+1].readHexChar
|
||||
inc(i)
|
||||
|
||||
proc toHex*(ba: seq[byte]): string {.noSideEffect, noInit.}=
|
||||
## Convert a big-endian byte-array to its hex representation
|
||||
proc toHex*[N: static[int]](ba: ByteArrayBE[N]): string {.noSideEffect.}=
|
||||
## Convert a big-endian byte array to its hex representation
|
||||
## Output is in lowercase
|
||||
##
|
||||
|
||||
const hexChars = "0123456789abcdef"
|
||||
|
||||
result = newString(2*N)
|
||||
for i in 0 ..< N:
|
||||
result[2*i] = hexChars[int ba[i] shr 4 and 0xF]
|
||||
result[2*i+1] = hexChars[int ba[i] and 0xF]
|
||||
|
||||
proc toHex*(ba: seq[byte]): string {.noSideEffect, noInit.}=
|
||||
## Convert a big-endian byte sequence to its hex representation
|
||||
## Output is in lowercase
|
||||
##
|
||||
## Warning ⚠: Do not use toHex for hex representation of Public Keys
|
||||
## Use the ``serialize`` proc:
|
||||
## - PublicKey is actually 2 separate numbers corresponding to coordinate on elliptic curve
|
||||
## - It is resistant against timing attack
|
||||
|
||||
let N = ba.len
|
||||
const hexChars = "0123456789abcdef"
|
||||
|
@ -2,11 +2,49 @@
|
||||
# MIT Licence
|
||||
# Copyright (c) 2016 Mamy Ratsimbazafy
|
||||
|
||||
# ########### Number of bits to represent a number
|
||||
|
||||
# Compiler defined const: https://github.com/nim-lang/Nim/wiki/Consts-defined-by-the-compiler
|
||||
const withBuiltins = defined(gcc) or defined(clang)
|
||||
|
||||
when withBuiltins:
|
||||
proc builtin_clz(n: cuint): cint {.importc: "__builtin_clz", nodecl.}
|
||||
proc builtin_clz(n: culong): cint {.importc: "__builtin_clzl", nodecl.}
|
||||
proc builtin_clz(n: culonglong): cint {.importc: "__builtin_clzll", nodecl.}
|
||||
type TbuiltinSupported = cuint or culong or culonglong
|
||||
## Count Leading Zero with optimized builtins routines from GCC/Clang
|
||||
## Warning ⚠: if n = 0, clz is undefined
|
||||
|
||||
proc bit_length*[T: SomeInteger](n: T): T =
|
||||
## Calculates how many bits are necessary to represent the number
|
||||
|
||||
when withBuiltins and T is TbuiltinSupported:
|
||||
result = if n == T(0): 0 # Removing this branch would make divmod 4x faster :/
|
||||
else: T.sizeof * 8 - builtin_clz(n)
|
||||
|
||||
else:
|
||||
var x = n
|
||||
while x != T(0):
|
||||
x = x shr 1
|
||||
inc(result)
|
||||
|
||||
|
||||
# ########### Integer math
|
||||
|
||||
proc isOdd*[T: SomeInteger](i: T): bool {.inline, noSideEffect.} =
|
||||
(i and 1.T) != 0
|
||||
|
||||
# ############
|
||||
proc isqrt*[T: SomeInteger](n: T): T =
|
||||
## Integer square root, return the biggest squarable number under n
|
||||
## Computation via Newton method
|
||||
result = n
|
||||
var y = (2.T shl ((n.bit_length() + 1) shr 1)) - 1
|
||||
while y < result:
|
||||
result = y
|
||||
y = (result + n div result) shr 1
|
||||
|
||||
|
||||
# ############ Efficient divmod
|
||||
|
||||
type
|
||||
ldiv_t {.bycopy, importc: "ldiv_t", header:"<stdlib.h>".} = object
|
||||
@ -34,7 +72,7 @@ proc divmod*[T: SomeUnsignedInt](a, b: T): tuple[quot, rem: T] {.inline.}=
|
||||
# Hopefully the compiler does its work properly
|
||||
(a div b, a mod b)
|
||||
|
||||
# ############
|
||||
# ############ Modular arithmetics
|
||||
|
||||
proc addmod*[T: SomeInteger](a, b, m: T): T =
|
||||
## Modular addition
|
||||
|
@ -4,8 +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 # Not on Nimble yet: https://github.com/numforge/number-theory
|
||||
|
||||
import ./intmath
|
||||
|
||||
proc isPrime*(x: SomeUnsignedInt): bool {.noSideEffect.}=
|
||||
for i in 2 .. isqrt x:
|
||||
|
29
tests/all_tests.nim
Normal file
29
tests/all_tests.nim
Normal file
@ -0,0 +1,29 @@
|
||||
# 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 ../src/ethash, unittest, strutils,
|
||||
keccak_tiny
|
||||
|
||||
|
||||
suite "Base hashing algorithm":
|
||||
test "FNV hashing":
|
||||
|
||||
let
|
||||
x = 1235'u32
|
||||
y = 9999999'u32
|
||||
FNV_PRIME = 0x01000193'u32
|
||||
|
||||
check: ((FNV_PRIME * x) xor y) == fnv(x, y)
|
||||
|
||||
|
||||
test "Keccak-256 - Note: spec mentions sha3 but it is Keccak":
|
||||
|
||||
let
|
||||
input = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
|
||||
expected = "2b5ddf6f4d21c23de216f44d5e4bdc68e044b71897837ea74c83908be7037cd7".toUpperASCII
|
||||
actual = toUpperASCII($input.keccak_256) # using keccak built-in conversion proc
|
||||
actual2 = cast[array[256 div 8, byte]](input.keccak_256).toHex.toUpperAscii
|
||||
|
||||
check: expected == actual
|
||||
check: expected == actual2
|
||||
|
Loading…
x
Reference in New Issue
Block a user