port eth2_digest speedups to eth_hash (#716)

* port eth2_digest speedups to eth_hash

* faster comparison
* `hash` integration
* lower-case printing
This commit is contained in:
Jacek Sieka 2024-08-08 07:44:15 +02:00 committed by GitHub
parent ebfe63b9b6
commit 864d54467e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 37 additions and 19 deletions

View File

@ -2,7 +2,7 @@ import stint, ./common/eth_hash
type UInt2048 = StUint[2048] type UInt2048 = StUint[2048]
iterator chunksForBloom(h: MDigest[256]): array[2, uint8] = iterator chunksForBloom(h: KeccakHash): array[2, uint8] =
yield [h.data[0], h.data[1]] yield [h.data[0], h.data[1]]
yield [h.data[2], h.data[3]] yield [h.data[2], h.data[3]]
yield [h.data[4], h.data[5]] yield [h.data[4], h.data[5]]
@ -12,25 +12,25 @@ proc chunkToBloomBits(chunk: array[2, uint8]): UInt2048 =
let l = chunk[1].int let l = chunk[1].int
one(UInt2048) shl ((l + (h shl 8)) and 2047) one(UInt2048) shl ((l + (h shl 8)) and 2047)
iterator bloomBits(h: MDigest[256]): UInt2048 = iterator bloomBits(h: KeccakHash): UInt2048 =
for chunk in chunksForBloom(h): for chunk in chunksForBloom(h):
yield chunkToBloomBits(chunk) yield chunkToBloomBits(chunk)
type BloomFilter* = object type BloomFilter* = object
value*: UInt2048 value*: UInt2048
proc incl*(f: var BloomFilter, h: MDigest[256]) = proc incl*(f: var BloomFilter, h: KeccakHash) =
for bits in bloomBits(h): for bits in bloomBits(h):
f.value = f.value or bits f.value = f.value or bits
proc init*(_: type BloomFilter, h: MDigest[256]): BloomFilter = proc init*(_: type BloomFilter, h: KeccakHash): BloomFilter =
result.incl(h) result.incl(h)
# TODO: The following 2 procs should be one genric, but it doesn't compile. Nim bug? # TODO: The following 2 procs should be one genric, but it doesn't compile. Nim bug?
proc incl*(f: var BloomFilter, v: string) = f.incl(keccakHash(v)) proc incl*(f: var BloomFilter, v: string) = f.incl(keccakHash(v))
proc incl*(f: var BloomFilter, v: openArray[byte]) = f.incl(keccakHash(v)) proc incl*(f: var BloomFilter, v: openArray[byte]) = f.incl(keccakHash(v))
proc contains*(f: BloomFilter, h: MDigest[256]): bool = proc contains*(f: BloomFilter, h: KeccakHash): bool =
for bits in bloomBits(h): for bits in bloomBits(h):
if (f.value and bits).isZero: return false if (f.value and bits).isZero: return false
return true return true

View File

@ -9,16 +9,19 @@
## keccak256 is used across ethereum as the "default" hash function and this ## keccak256 is used across ethereum as the "default" hash function and this
## module provides a type and some helpers to produce such hashes ## module provides a type and some helpers to produce such hashes
import import std/hashes
nimcrypto/[keccak, hash] import nimcrypto/[keccak, utils]
import nimcrypto/hash except `$`
from nimcrypto/utils import bytesToHex
export export
keccak.update, keccak.finish, hash keccak.update, keccak.finish, hash.fromHex, hash.toDigest, hashes.Hash
type type
KeccakHash* = MDigest[256] KeccakHash* = MDigest[256]
## A hash value computed using keccak256 ## A hash value computed using keccak256
## note: this aliases Eth2Digest too, which uses a different hash! ## note: this aliases Eth2Digest too, which uses a different hash function!
template withKeccakHash*(body: untyped): KeccakHash = template withKeccakHash*(body: untyped): KeccakHash =
## This little helper will init the hash function and return the sliced ## This little helper will init the hash function and return the sliced
@ -44,3 +47,23 @@ func keccakHash*(a, b: openArray[byte]): KeccakHash =
withKeccakHash: withKeccakHash:
h.update a h.update a
h.update b h.update b
func `$`*(v: KeccakHash): string =
var res = newString((len(v.data) shl 1))
discard bytesToHex(v.data, res, {HexFlags.LowerCase})
res
template hash*(x: KeccakHash): Hash =
## Hash for digests for Nim hash tables
# digests are already good hashes
var h {.noinit.}: Hash
copyMem(addr h, unsafeAddr x.data[0], static(sizeof(Hash)))
h
func `==`*(a, b: KeccakHash): bool =
when nimvm:
a.data == b.data
else:
# nimcrypto uses a constant-time comparison for all MDigest types which for
# KeccakHash is unnecessary - the type should never hold a secret!
equalMem(unsafeAddr a.data[0], unsafeAddr b.data[0], sizeof(a.data))

View File

@ -10,8 +10,8 @@ import
export eth_hash, rlp export eth_hash, rlp
proc read*(rlp: var Rlp, T: typedesc[MDigest]): T = proc read*(rlp: var Rlp, T: typedesc[KeccakHash]): T =
result.data = rlp.read(type(result.data)) result.data = rlp.read(type(result.data))
proc append*(rlpWriter: var RlpWriter, a: MDigest) = proc append*(rlpWriter: var RlpWriter, a: KeccakHash) =
rlpWriter.append(a.data) rlpWriter.append(a.data)

View File

@ -17,6 +17,8 @@ import
./eth_hash, ./eth_hash,
./eth_times ./eth_times
from nimcrypto/hash import MDigest
export export
results, results,
stint, stint,

View File

@ -302,7 +302,7 @@ proc appendImpl(self: var RlpWriter, data: tuple) {.inline.} =
# score in order to facilitate easier overloading with user types: # score in order to facilitate easier overloading with user types:
template append*[T](w: var RlpWriter; data: T) = template append*[T](w: var RlpWriter; data: T) =
when data is (SomeSignedInt|enum|bool): when data is (SomeSignedInt|enum|bool):
when data is SomeSignedInt: when false and data is SomeSignedInt:
# TODO potentially remove signed integer support - we should never make it # TODO potentially remove signed integer support - we should never make it
# this far! # this far!
{.warning: "Signed integers cannot reliably be encoded using RLP".} {.warning: "Signed integers cannot reliably be encoded using RLP".}
@ -314,7 +314,6 @@ proc initRlpList*(listSize: int): RlpWriter =
result = initRlpWriter() result = initRlpWriter()
startList(result, listSize) startList(result, listSize)
# TODO: This should return a lent value
template finish*(self: RlpWriter): seq[byte] = template finish*(self: RlpWriter): seq[byte] =
doAssert self.pendingLists.len == 0, "Insufficient number of elements written to a started list" doAssert self.pendingLists.len == 0, "Insufficient number of elements written to a started list"
self.output self.output

View File

@ -1,5 +1,4 @@
import import
stew/byteutils,
./trie_defs ./trie_defs
export trie_defs export trie_defs
@ -10,8 +9,3 @@ template checkValidHashZ*(x: untyped) =
template isZeroHash*(x: openArray[byte]): bool = template isZeroHash*(x: openArray[byte]): bool =
x.len == 0 x.len == 0
proc hashFromHex*(bits: static[int], input: string): MDigest[bits] =
MDigest(data: hexToByteArray[bits div 8](input))
template hashFromHex*(s: static[string]): untyped = hashFromHex(s.len * 4, s)