Remove the hard to read concat procs

This commit is contained in:
mratsim 2018-02-23 15:37:33 +01:00
parent 3dd362990e
commit f11a6a2cde
4 changed files with 83 additions and 81 deletions

View File

@ -4,7 +4,7 @@
import math, sequtils, algorithm, import math, sequtils, algorithm,
keccak_tiny keccak_tiny
import ./private/[primes, casting, functional, intmath, concat] import ./private/[primes, casting, functional, intmath]
export toHex, hexToByteArrayBE, hexToSeqBytesBE, toByteArrayBE export toHex, hexToByteArrayBE, hexToSeqBytesBE, toByteArrayBE
export keccak_tiny export keccak_tiny
@ -147,7 +147,7 @@ proc calc_dataset*(full_size: Natural, cache: seq[Hash[512]]): seq[Hash[512]] {.
# ############################################################################### # ###############################################################################
# Main loop # Main loop
type HashimotoHash = tuple[mix_digest: array[8, uint32], value: Hash[256]] type HashimotoHash = tuple[mix_digest: Hash[256], value: Hash[256]]
# TODO use Hash as a result type # TODO use Hash as a result type
type DatasetLookup = proc(i: Natural): Hash[512] {.noSideEffect.} type DatasetLookup = proc(i: Natural): Hash[512] {.noSideEffect.}
@ -157,20 +157,32 @@ proc hashimoto(header: Hash[256],
dataset_lookup: DatasetLookup dataset_lookup: DatasetLookup
): HashimotoHash {.noInit, noSideEffect.}= ): HashimotoHash {.noInit, noSideEffect.}=
let let
n = uint32 full_size div HASH_BYTES # check div operator, in spec it's Python true division n = uint32 full_size div HASH_BYTES
w = uint32 MIX_BYTES div WORD_BYTES # TODO: review word bytes: uint32 vs uint64 w = uint32 MIX_BYTES div WORD_BYTES
mixhashes = uint32 MIX_BYTES div HASH_BYTES mixhashes = uint32 MIX_BYTES div HASH_BYTES
# combine header+nonce into a 64 byte seed
s = concat_hash(header, nonce).toU512 assert full_size mod HASH_BYTES == 0
assert MIX_BYTES mod HASH_BYTES == 0
# combine header+nonce into a 64 byte seed
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_words = cast[ptr array[16, uint32]](addr s) # Alias for to interpret s as an uint32 array
s_bytes[0..<32] = header.toByteArrayBE # We first populate the first 40 bytes of s with the concatenation
s_bytes[32..<40] = nonce.toByteArrayBE
s = keccak_512 s_bytes[0..<40]
# start the mix with replicated s # start the mix with replicated s
var mix{.noInit.}: array[32, uint32] # MIX_BYTES / HASH_BYTES * sizeof(s) => 1024 assert MIX_BYTES div HASH_BYTES == 2
mix[0..<16] = s var mix{.noInit.}: array[32, uint32]
mix[16..<32] = s mix[0..<16] = s_words[]
mix[16..<32] = s_words[]
# mix in random dataset nodes # mix in random dataset nodes
for i in 0'u32 ..< ACCESSES: for i in 0'u32 ..< ACCESSES:
let p = fnv(i.uint32 xor s[0].uint32, mix[i mod w]) mod (n div mixhashes) * mixhashes let p = fnv(i xor s_words[0], mix[i mod w]) mod (n div mixhashes) * mixhashes
# 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
@ -179,13 +191,19 @@ proc hashimoto(header: Hash[256],
mix = zipMap(mix, newdata, fnv(x, y)) mix = zipMap(mix, newdata, fnv(x, y))
# compress mix (aka result.mix_digest) # compress mix
# TODO: what is the representation of mix during FNV? big-endian, native host endianess? var cmix: array[8, uint32]
for i in countup(0, mix.len - 1, 4): for i in countup(0, mix.len - 1, 4):
result.mix_digest[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])
result.value = keccak256 concat_hash(s, result.mix_digest) result.mix_digest = cast[Hash[256]](
mapArray(cmix, x.toByteArrayBE) # Each uint32 must be changed to Big endian
)
var concat{.noInit.}: array[64 + 32, byte]
concat[0..<64] = s_bytes[]
concat[64..<96] = cast[array[32, byte]](cmix)
result.value = keccak_256(concat)
proc hashimoto_light*(full_size:Natural, cache: seq[Hash[512]], proc hashimoto_light*(full_size:Natural, cache: seq[Hash[512]],
header: Hash[256], nonce: uint64): HashimotoHash {.noSideEffect, inline.} = header: Hash[256], nonce: uint64): HashimotoHash {.noSideEffect, inline.} =

View File

@ -1,35 +0,0 @@
# 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,
./casting
proc concat_hash*(header: Hash[256], nonce: uint64): Hash[512] {.noSideEffect, inline, noInit.} =
# Can't take compile-time sizeof of arrays in objects: https://github.com/nim-lang/Nim/issues/5802
var cat{.noInit.}: array[256 div 8 + nonce.sizeof, byte]
let nonceBE = nonce.toByteArrayBE # Big endian representation of the number
# Concatenate header and the big-endian nonce
for i, b in header.data:
cat[i] = b
for i, b in nonceBE:
cat[i + header.sizeof] = b
result = keccak512 cat
proc concat_hash*(s: U512, cmix: array[8, uint32]): array[(512 + 8 * 32) div 8, byte] {.noSideEffect, inline, noInit.} =
# Concatenate header and the big-endian nonce
let sb = s.toByteArrayBE
for i, b in sb:
result[i] = b
# TODO: Do we need to convert cmix to Big Endian??
let cmixb = cast[ByteArrayBE[32]](cmix)
for i, b in cmixb:
let offset = sb.len + i
result[offset] = b

View File

@ -31,7 +31,26 @@ template zipMap*[N: static[int], T, U](
var result: array[N, outType] var result: array[N, outType]
for i, x {.inject.}, y {.inject.} in enumerateZip(a, b): for i, x {.inject.}, y {.inject.} in enumerateZip(a, b):
{.unroll: 8.} {.unroll: 4.}
result[i] = op result[i] = op
result
template mapArray*[N: static[int], T](
a: array[N, T],
op: untyped): untyped =
## inline map operation
type outType = type((
block:
var x{.inject.}: T;
op
))
var result: array[N, outType]
for i, x {.inject.} in a:
{.unroll: 4.}
result[i] = op
result result

View File

@ -185,47 +185,46 @@ suite "Dagger hashimoto computation":
let full_result = hashimoto_full(full_size, dataset, header, 0) let full_result = hashimoto_full(full_size, dataset, header, 0)
# Check not null # Check not null
var zero_array: array[8, uint32]
var zero_hash : Hash[256] var zero_hash : Hash[256]
check: light_result.mix_digest != zero_array check: light_result.mix_digest != zero_hash
check: light_result.value != zero_hash check: light_result.value != zero_hash
check: light_result == full_result check: light_result == full_result
# test "Light compute": test "Light compute":
# # https://github.com/paritytech/parity/blob/05f47b635951f942b493747ca3bc71de90a95d5d/ethash/src/compute.rs#L372-L394 # https://github.com/paritytech/parity/blob/05f47b635951f942b493747ca3bc71de90a95d5d/ethash/src/compute.rs#L372-L394
# let hash = cast[Hash[256]]([ let hash = cast[Hash[256]]([
# byte 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, byte 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3,
# 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94,
# 0x05, 0x52, 0x7d, 0x72 0x05, 0x52, 0x7d, 0x72
# ]) ])
# let expected_mix_hash = cast[array[8, uint32]]([ let expected_mix_hash = cast[Hash[256]]([
# byte 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, byte 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
# 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
# 0x64, 0x31, 0xab, 0x6d 0x64, 0x31, 0xab, 0x6d
# ]) ])
# let expected_boundary = cast[Hash[256]]([ let expected_boundary = cast[Hash[256]]([
# byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
# 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
# 0xe9, 0x7e, 0x53, 0x84 0xe9, 0x7e, 0x53, 0x84
# ]) ])
# let nonce = 0xd7b3ac70a301a249'u64 let nonce = 0xd7b3ac70a301a249'u64
# ## difficulty = 0x085657254bd9u64 ## difficulty = 0x085657254bd9u64
# let blk = 486382'u # block number let blk = 486382'u # block number
# let light_cache = mkcache(blk.get_cache_size, blk.get_seedhash) let light_cache = mkcache(blk.get_cache_size, blk.get_seedhash)
# let r = hashimoto_light(blk.get_data_size, let r = hashimoto_light(blk.get_data_size,
# light_cache, light_cache,
# blk.get_seedhash, blk.get_seedhash,
# nonce nonce
# ) )
# check: r.mix_digest == expected_mix_hash check: r.mix_digest == expected_mix_hash
# check: r.value == expected_boundary check: r.value == expected_boundary
suite "Real blocks test": suite "Real blocks test":
@ -245,9 +244,10 @@ suite "Real blocks test":
0x495732e0ed7a801c'u 0x495732e0ed7a801c'u
) )
## Todo: blockhash is not actually Hex
check: light.value == cast[Hash[256]]( check: light.value == cast[Hash[256]](
hexToByteArrayBE[32]("00000b184f1fdd88bfd94c86c39e65db0c36144d5e43f745f722196e730cb614") hexToByteArrayBE[32]("00000b184f1fdd88bfd94c86c39e65db0c36144d5e43f745f722196e730cb614")
) )
check: light.mixDigest == cast[array[8, uint32]]( check: light.mixDigest == cast[Hash[256]](
hexToByteArrayBE[32]("2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5") hexToByteArrayBE[32]("2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5")
) )