From 35705164ec9419b7a5aab3a449446f2156ca1726 Mon Sep 17 00:00:00 2001 From: advaita-saha Date: Sat, 27 Sep 2025 15:39:20 +0530 Subject: [PATCH 1/4] add new nim-eth with keccak constantine --- .gitmodules | 3 +++ vendor/constantine | 1 + vendor/nim-eth | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 160000 vendor/constantine diff --git a/.gitmodules b/.gitmodules index 289491594b..6ee0551bce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -226,3 +226,6 @@ path = vendor/nim-minilru url = https://github.com/status-im/nim-minilru.git branch = master +[submodule "vendor/constantine"] + path = vendor/constantine + url = https://github.com/mratsim/constantine diff --git a/vendor/constantine b/vendor/constantine new file mode 160000 index 0000000000..d6aae1eca3 --- /dev/null +++ b/vendor/constantine @@ -0,0 +1 @@ +Subproject commit d6aae1eca3775d6317e11b169edef0249162ce22 diff --git a/vendor/nim-eth b/vendor/nim-eth index 0b67655b1a..97dd82703f 160000 --- a/vendor/nim-eth +++ b/vendor/nim-eth @@ -1 +1 @@ -Subproject commit 0b67655b1af6ebbcad3a36c79b7257c966158e09 +Subproject commit 97dd82703fae8ba6d8eeb3f03f657ef894199749 From 33b65ca516b62109eaae892b4c4f722eb1e1b04f Mon Sep 17 00:00:00 2001 From: advaita-saha Date: Sat, 27 Sep 2025 17:45:47 +0530 Subject: [PATCH 2/4] hotfix --- execution_chain/evm/evm_errors.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/execution_chain/evm/evm_errors.nim b/execution_chain/evm/evm_errors.nim index 9de954088b..ef6ca244fa 100644 --- a/execution_chain/evm/evm_errors.nim +++ b/execution_chain/evm/evm_errors.nim @@ -77,10 +77,10 @@ template evmErr*(errCode): auto = # revertSelector is a special function selector for revert reason unpacking -const revertSelector = keccak256(toBytes("Error(string)")).data[0..3] +const revertSelector = @[byte 8, byte 195, byte 121, byte 160] # keccak256(toBytes("Error(string)")).data[0..3] # panicSelector is a special function selector for panic reason unpacking -const panicSelector = keccak256(toBytes("Panic(uint256)")).data[0..3] +const panicSelector = @[byte 78, byte 72, byte 123, byte 113] # keccak256(toBytes("Panic(uint256)")).data[0..3] # panicReasons map is for readable panic codes # see this linkage for the details From d66a0006865702350160246f0d5fc01f3fdd83f6 Mon Sep 17 00:00:00 2001 From: advaita-saha Date: Fri, 3 Oct 2025 15:40:13 +0530 Subject: [PATCH 3/4] add benchmarking code --- tests/test_benchmark.nim | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 tests/test_benchmark.nim diff --git a/tests/test_benchmark.nim b/tests/test_benchmark.nim new file mode 100644 index 0000000000..e69bb1e6dd --- /dev/null +++ b/tests/test_benchmark.nim @@ -0,0 +1,175 @@ +# Nimbus +# Copyright (c) 2025 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + std/[monotimes, times, math, strformat], + eth/common, + nimcrypto/keccak, + constantine/hashes/h_keccak + +type + KECCACK256* = h_keccak.KeccakContext[256, 0x01] + +func keccak_nimcrypto*(input: openArray[byte]): Hash32 = + var ctx: keccak.keccak256 + ctx.update(input) + ctx.finish().to(Hash32) + +func keccak_nimcrypto*(input: openArray[char]): Hash32 = + keccak_nimcrypto(input.toOpenArrayByte(0, input.high)) + +func keccak_constantine*(input: openArray[byte]): Hash32 = + var + ctx: KECCACK256 + buff: array[32, byte] + ctx.update(input) + ctx.finish(buff) + Hash32(buff) + +func keccak_constantine*(input: openArray[char]): Hash32 = + keccak_constantine(input.toOpenArrayByte(0, input.high)) + +const + INPUT_32_BYTE = "1234567890abcdef1234567890abcdef" + INPUT_64_BYTE = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + INPUT_128_BYTE = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + INPUT_256_BYTE = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + +# Utility: turn string into a seq[byte] once, to avoid per-iter conversion costs. +proc toBytesOnce(s: string): seq[byte] = + result = newSeq[byte](s.len) + if s.len > 0: + # Copy raw bytes of the string into result + when compiles(copyMem): + copyMem(addr result[0], unsafeAddr s[0], s.len) + else: + for i, c in s: result[i] = byte(c) + +# Benchmark runner +proc bench(name: string, + hashFn: proc(data: openArray[byte]): Hash32 {.noSideEffect.}, + data: openArray[byte], + iters: int) = + # Warm-up + var acc: uint32 = 0 + for _ in 0 ..< min(iters, 10_000): + let h = hashFn(data) + # consume 4 bytes to avoid DCE; Hash32 is 32 bytes + acc = acc xor cast[ptr uint32](unsafeAddr h)[] + + # Timed run + let start = getMonoTime() + for _ in 0 ..< iters: + let h = hashFn(data) + acc = acc xor cast[ptr uint32](unsafeAddr h)[] + let dur = getMonoTime() - start + + # Metrics + let nsTotal = dur.inNanoseconds.float + let nsPerOp = nsTotal / iters.float + let bytesPerOp = data.len.float + let mbps = (bytesPerOp * iters.float) / (dur.inSeconds.float * 1024.0 * 1024.0) + + echo fmt"{name:>30} | size={data.len:>4} iters={iters:>9} ns/op={nsPerOp:>10.1f} MB/s={mbps:>8.1f} (acc={acc})" + +# Choose iteration counts to keep each test ~200–400ms on a typical dev box. +# Scale inversely with input length so total hashed bytes per test are similar. +proc chooseIters(size: int): int = + let targetBytes = 64 * 1024 * 1024 # ~64 MiB per test + max(1, targetBytes div max(1, size)) + + +type DataKind = enum + dkZeros, dkPattern, dkPRG + +proc genData(kind: DataKind; size: int; seed: string = "eth-blocks"): seq[byte] = + ## Deterministic payload generator for benchmarking. + result = newSeq[byte](size) + case kind + of dkZeros: + # already zero-initialized + discard + of dkPattern: + # Convert pattern string to an addressable array + const patStr = "1234567890abcdef" + var pat: array[patStr.len, byte] + for i, c in patStr: + pat[i] = byte(c) + + var i = 0 + while i < size: + let chunk = min(pat.len, size - i) + when compiles(copyMem): + copyMem(addr result[i], addr pat[0], chunk) + else: + for j in 0 ..< chunk: + result[i + j] = pat[j] + i += chunk + + of dkPRG: + # Keccak(counter | seed) stream; reproducible across runs. + var off = 0 + var ctr: uint64 = 0 + var outblk: array[32, byte] + while off < size: + var ctx: keccak.keccak256 + ctx.update(cast[array[8, byte]](ctr)) # little-endian ctr bytes + ctx.update(seed) + let digest = ctx.finish() + # Write 32 bytes at a time + when compiles(copyMem): + copyMem(addr outblk[0], unsafeAddr digest.data[0], 32) + else: + for i in 0 ..< 31: outblk[i] = digest.data[i] + let chunk = min(32, size - off) + when compiles(copyMem): + copyMem(addr result[off], addr outblk[0], chunk) + else: + for i in 0 ..< chunk: result[off + i] = outblk[i] + inc ctr + inc off, chunk + +# Suggested EL-like sizes (bytes) +const BlockSizes = [ + 32 * 1024, # 32 KiB + 64 * 1024, # 64 KiB + 128 * 1024, # 128 KiB + 256 * 1024, # 256 KiB + 512 * 1024, # 512 KiB + 1 * 1024 * 1024, # 1 MiB (stress) + 2 * 1024 * 1024, # 2 MiB (stress) + 4 * 1024 * 1024, + 5 * 1024 * 1024, + 6 * 1024 * 1024 +] + +when isMainModule: + let inputs = [ + ("32B", INPUT_32_BYTE.toBytesOnce()), + ("64B", INPUT_64_BYTE.toBytesOnce()), + ("128B", INPUT_128_BYTE.toBytesOnce()), + ("256B", INPUT_256_BYTE.toBytesOnce()) + ] + + echo "== Keccak Bench: nimcrypto vs constantine ==" + for (label, buf) in inputs: + let iters = chooseIters(buf.len) + bench(&"nimcrypto {label}", keccak_nimcrypto, buf, iters) + bench(&"constantine {label}", keccak_constantine, buf, iters) + + for sz in BlockSizes: + for kind in [dkZeros, dkPattern, dkPRG]: + let label = + (case kind + of dkZeros: "zeros" + of dkPattern: "pattern" + of dkPRG: "prg") + let buf = genData(kind, sz) + let iters = chooseIters(buf.len) # reuse your chooseIters() + bench(fmt"nimcrypto {label} {sz div 1024}KiB", keccak_nimcrypto, buf, iters) + bench(fmt"constantine {label} {sz div 1024}KiB", keccak_constantine, buf, iters) + From fa5825fdd00015c709d66baf1e87576ea8a1f555 Mon Sep 17 00:00:00 2001 From: Advaita Saha Date: Fri, 3 Oct 2025 21:41:25 +0530 Subject: [PATCH 4/4] fix benchmark --- tests/test_benchmark.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_benchmark.nim b/tests/test_benchmark.nim index e69bb1e6dd..eaad9d439a 100644 --- a/tests/test_benchmark.nim +++ b/tests/test_benchmark.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - std/[monotimes, times, math, strformat], + std/[monotimes, times, strformat], eth/common, nimcrypto/keccak, constantine/hashes/h_keccak