From 786337e9303a923e91224f33611cfb728f40a3e2 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Sun, 11 Nov 2018 19:21:47 -0600 Subject: [PATCH 1/3] tree_ssz: naive implementation of treeHash as seen in https://github.com/ethereum/eth2.0-specs/pull/120 --- beacon_chain/tree_hash.nim | 112 +++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 beacon_chain/tree_hash.nim diff --git a/beacon_chain/tree_hash.nim b/beacon_chain/tree_hash.nim new file mode 100644 index 000000000..9800922a6 --- /dev/null +++ b/beacon_chain/tree_hash.nim @@ -0,0 +1,112 @@ +import nimcrypto, eth_common, endians, sequtils, algorithm + +# Sample treehash implementation based on: +# https://github.com/ethereum/eth2.0-specs/pull/120 +# Probably wrong - the spec is pretty bare-bones and no test vectors yet + +const CHUNK_SIZE = 128 + +# XXX varargs openarray, anyone? +func hash(a: openArray[byte]): array[32, byte] = + var h: blake2_512 + h.init() + h.update(a) + var tmp = h.finish().data + copyMem(result.addr, tmp.addr, 32) + +func hash(a, b: openArray[byte]): array[32, byte] = + var h: blake2_512 + h.init() + h.update(a) + h.update(b) + var tmp = h.finish().data + copyMem(result.addr, tmp.addr, 32) + +func nextPowerOf2(v: uint32): uint32 = + result = v - 1 + result = result or (result shr 1) + result = result or (result shr 2) + result = result or (result shr 4) + result = result or (result shr 8) + result = result or (result shr 16) + inc result + +func roundUpTo(v, to: int): int = + ## Round up to an even boundary of `to` + ((v + to - 1) div to) * to + +# Concatenate a list of homogeneous objects into data and pad it +proc listToGlob(lst: seq[seq[byte]]): seq[byte] = + for x in lst: + var y = x + y.setLen(nextPowerOf2(len(x).uint32)) + result.add(y) + + # Pad to chunksize + result.setLen(result.len().roundUpTo(CHUNK_SIZE)) + +# XXX: er, how is this _actually_ done? +func empty(T: typedesc): T = discard +const emptyChunk = @(empty(array[CHUNK_SIZE, byte])) + +proc merkleHash(lst: seq[seq[byte]]): array[32, byte] = + ## Merkle tree hash of a list of items + # XXX: seq-of-seq looks weird... + + # Turn list into padded data + var data = listToGlob(lst) + + # Store length of list (to compensate for non-bijectiveness of padding) + var dataLen: array[32, byte] + var lstLen = uint64(len(lst)) + bigEndian64(dataLen[32-8].addr, lstLen.addr) + + # Divide into chunks + var chunkz: seq[seq[byte]] + for i in countup(0, data.len - 1, CHUNK_SIZE): + chunkz.add data[i.. 1: + if chunkz.len() mod 2 == 1: + chunkz.add emptyChunk + for i in 0..<(chunkz.len div 2): + # As tradition dictates - one feature, at least one nim bug: + # https://github.com/nim-lang/Nim/issues/9684 + let tmp = @(hash(chunkz[i * 2], chunkz[i * 2 + 1])) + chunkz[i] = tmp + + chunkz.setLen(chunkz.len div 2) + + result = hash(chunkz[0], dataLen) + +proc treeHash*(x: SomeInteger): seq[byte] = + var v: array[x.sizeof, byte] + copyMem(v.addr, x.unsafeAddr, x.sizeof) + + var res: array[x.sizeof, byte] + when x.sizeof == 8: bigEndian64(res.addr, v.addr) + elif x.sizeof == 4: bigEndian32(res.addr, v.addr) + elif x.sizeof == 2: bigEndian16(res.addr, v.addr) + elif x.sizeof == 1: res = v + else: {.fatal: "boink: " & $x.sizeof .} + result = @res + +proc treeHash*(x: EthAddress): seq[byte] = @x +proc treeHash*(x: MDigest): seq[byte] = @(x.data) +proc treeHash*(x: seq[byte]): seq[byte] = @(hash(x)) # XXX: hash96 also! +proc treeHash*[T: seq](x: T): seq[byte] = + var tmp: seq[seq[byte]] + for v in x: + tmp.add treeHash(v) + result = merkleHash(tmp) + +proc treeHash*[T](x: T): seq[byte] = + # XXX: could probaby compile-time-macro-sort fields... + var fields: seq[tuple[name: string, value: seq[byte]]] + for name, field in x.fieldPairs: + fields.add (name, treeHash(field)) + + var tmp: seq[byte] + for name, value in fields.sortedByIt(it.name): + tmp.add value.value + result = @(hash(tmp)) From 7a226b0be6b5fc965ba910a6844a25958b30cb9b Mon Sep 17 00:00:00 2001 From: mratsim Date: Mon, 12 Nov 2018 09:34:03 +0100 Subject: [PATCH 2/3] Comment out block processing --- tests/all_tests.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 89bb553b3..fa3cf66c9 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -6,5 +6,5 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ./test_ssz, - ./test_block_processing + ./test_ssz + # ./test_block_processing From dab75a76a2d8b4f6883da621315c17d2711c578a Mon Sep 17 00:00:00 2001 From: mratsim Date: Mon, 12 Nov 2018 09:46:16 +0100 Subject: [PATCH 3/3] comment out aggregated sig + make SSZ compile again --- beacon_chain/datatypes.nim | 4 ++-- beacon_chain/ssz.nim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/beacon_chain/datatypes.nim b/beacon_chain/datatypes.nim index ebf1e8242..a699824b7 100644 --- a/beacon_chain/datatypes.nim +++ b/beacon_chain/datatypes.nim @@ -28,7 +28,7 @@ type # Alias BLSPublicKey* = VerKey BLSsig* = Signature - BLSaggregateSig* = AggregatedSignature + # BLSaggregateSig* = AggregatedSignature Blake2_256_Digest* = Hash256 # TODO change to Blake2b-512[0 ..< 32] see https://github.com/status-im/nim-beacon-chain/issues/3 Uint24* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around @@ -96,7 +96,7 @@ type attester_bitfield*: IntSet # Who is participating justified_slot*: int64 justified_block_hash: Blake2_256_Digest - aggregate_sig*: BLSaggregateSig # The actual signature + # aggregate_sig*: BLSaggregateSig # The actual signature ValidatorStatusCodes* {.pure.} = enum PendingActivation = 0 diff --git a/beacon_chain/ssz.nim b/beacon_chain/ssz.nim index 341fcb99d..133e3c72e 100644 --- a/beacon_chain/ssz.nim +++ b/beacon_chain/ssz.nim @@ -9,7 +9,7 @@ # See https://github.com/ethereum/beacon_chain/issues/100 # and https://github.com/ethereum/beacon_chain/tree/master/ssz -import ./datatypes, eth_common, endians, typetraits, options +import ./datatypes, eth_common, endians, typetraits, options, nimcrypto # ################### Helper functions ################################### func `+`[T](p: ptr T, offset: int): ptr T {.inline.}=