nim-eth/eth/common/hashes.nim

105 lines
3.8 KiB
Nim

# eth
# Copyright (c) 2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [].}
## Keccak256 hash function use thoughout the ethereum execution specification
## https://github.com/ethereum/execution-specs/blob/51fac24740e662844446439ceeb96a460aae0ba0/src/ethereum/crypto/hash.py
##
## The execution spec also declares a Hash64 type of mainly historical interest.
## Its usage was limited to ethash, the proof-of-work algorithm that has been
## replaced with proof-of-stake.
import std/[typetraits, hashes], nimcrypto/keccak, ./base, stew/assign2
export hashes, keccak.update, keccak.finish
type
Hash32* = distinct Bytes32
## https://github.com/ethereum/execution-specs/blob/51fac24740e662844446439ceeb96a460aae0ba0/src/ethereum/crypto/hash.py#L19
Root* = Hash32
## Alias used for MPT roots
VersionedHash* = Hash32
## Alias used for blob hashes whose first byte indicates what the payload
## actually is - primarily used with KZG commitments at the time of writing
## https://github.com/ethereum/execution-specs/blob/9b95554a88d2a8485f8180254d0f6a493a593fda/src/ethereum/crypto/kzg.py#L74C1-L74C38
const zeroHash32* = system.default(Hash32) ## Hash32 value consisting of all zeroes
template to*(v: array[32, byte], _: type Hash32): Hash32 =
Address(v)
template data*(v: Hash32): array[32, byte] =
distinctBase(v)
template `data=`*(a: Hash32, b: array[32, byte]) =
assign(distinctBase(a), b)
template copyFrom*(T: type Hash32, v: openArray[byte], start = 0): T =
## Copy up to N bytes from the given openArray, starting at `start` and
## filling any missing bytes with zero.
##
## This is a lenient function in that `v` may contain both fewer and more
## bytes than N and start might be out of bounds.
Hash32(Bytes32.copyFrom(v, start))
template default*(_: type Hash32): Hash32 =
# Avoid bad codegen where fixed bytes are zeroed byte-by-byte at call site
zeroHash32
func `==`*(a, b: Hash32): bool {.borrow.}
func hash*(a: Hash32): Hash {.inline.} =
# Hashes are already supposed to be random so we use a faster mixing function
var tmp {.noinit.}: array[4, uint64]
copyMem(addr tmp[0], addr a.data[0], sizeof(a))
cast[Hash](tmp[0] + tmp[1] + tmp[2] + tmp[3])
func toHex*(a: Hash32): string {.borrow.}
func to0xHex*(a: Hash32): string {.borrow.}
func `$`*(a: Hash32): string {.borrow.}
func fromHex*(_: type Hash32, s: openArray[char]): Hash32 {.raises: [ValueError].} =
Hash32(Bytes32.fromHex(s))
template to*(s: static string, _: type Hash32): Hash32 =
const hash = Hash32.fromHex(s)
hash
template hash32*(s: static string): Hash32 =
s.to(Hash32)
template to*(v: MDigest[256], _: type Hash32): Hash32 =
Hash32(v.data)
template to*(v: Hash32, _: type MDigest[256]): MDigest[256] =
var tmp {.noinit.}: MDigest[256]
assign(tmp.data, v.data)
tmp
const
emptyKeccak256* =
hash32"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
## Hash value of `keccak256([])`, ie the empty string
emptyRoot* = hash32"56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
## Hash value of `keccak256(rlp(null))` which corresponds to the encoding
## of an empty MPT trie
func keccak256*(input: openArray[byte]): Hash32 {.noinit.} =
var ctx: keccak.keccak256
ctx.update(input)
ctx.finish().to(Hash32)
func keccak256*(input: openArray[char]): Hash32 {.noinit.} =
keccak256(input.toOpenArrayByte(0, input.high))
template withKeccak256*(body: untyped): Hash32 =
var h {.inject.}: keccak.keccak256
body
h.finish().to(Hash32)