Skip to content

Commit c489b38

Browse files
authored
chore: add tests for keccak256 utf8 input (#33)
Add tests to ensure that our implementation accepts string as plain UTF8 string.
1 parent 0905bba commit c489b38

File tree

2 files changed

+385
-84
lines changed

2 files changed

+385
-84
lines changed

example/src/benchmarks/keccak256Benchmark.ts

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,30 @@ export async function benchmarkSmallString(): Promise<BenchmarkResult> {
9696
);
9797
}
9898

99+
// Benchmark address-length string (40 hex chars = 20 bytes when decoded, but 40 bytes as UTF-8)
100+
// This is a common scenario: hashing Ethereum address strings for checksumming, etc.
101+
export async function benchmarkAddressString(): Promise<BenchmarkResult> {
102+
const addressString = 'c24ef7796beeb7694e86fca4bafcdf955f16e6fc';
103+
104+
return benchmarkFunction(
105+
'Address String (40 chars)',
106+
() => keccak256(addressString),
107+
() => keccak_256(addressString),
108+
2000,
109+
);
110+
}
111+
99112
// Benchmark 32-byte private key
100113
export async function benchmarkPrivateKey(): Promise<BenchmarkResult> {
101114
const privateKeyHex =
102115
'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
116+
// Convert hex to bytes for fair comparison (strings are now UTF-8)
117+
const privateKeyBytes = hexToUint8Array('0x' + privateKeyHex);
103118

104119
return benchmarkFunction(
105-
'32-byte Private Key (hex string)',
106-
() => keccak256(privateKeyHex),
107-
() => keccak_256(hexToUint8Array('0x' + privateKeyHex)),
120+
'32-byte Private Key (Uint8Array)',
121+
() => keccak256(privateKeyBytes),
122+
() => keccak_256(privateKeyBytes),
108123
1500,
109124
);
110125
}
@@ -243,55 +258,57 @@ export async function benchmarkETHAddressChecksum(): Promise<BenchmarkResult> {
243258
);
244259
}
245260

246-
// Benchmark hex string vs bytes comparison
247-
export async function benchmarkHexVsBytes(): Promise<BenchmarkResult> {
248-
const hexString = '0123456789abcdef'.repeat(8); // 64 bytes as hex (no 0x prefix)
249-
const bytesInput = hexToUint8Array('0x' + hexString);
261+
// Benchmark UTF-8 string vs Uint8Array input comparison
262+
// Tests if there's any performance difference between input types
263+
export async function benchmarkStringVsBytes(): Promise<BenchmarkResult> {
264+
const testString = 'The quick brown fox jumps over the lazy dog. '.repeat(3); // ~135 chars
265+
const bytesInput = utf8ToBytes(testString);
250266

251-
const nativeTimes: number[] = [];
252-
const jsTimes: number[] = [];
267+
const stringTimes: number[] = [];
268+
const bytesTimes: number[] = [];
253269
const iterations = 1500;
254270

255271
// Warm up
256272
for (let i = 0; i < 10; i++) {
257-
keccak256(hexString);
273+
keccak256(testString);
258274
keccak256(bytesInput);
259275
}
260276

261-
// Benchmark native hex input
277+
// Benchmark string input (UTF-8)
262278
for (let i = 0; i < iterations; i++) {
263279
const start = performance.now();
264-
keccak256(hexString);
280+
keccak256(testString);
265281
const end = performance.now();
266-
nativeTimes.push(end - start);
282+
stringTimes.push(end - start);
267283
}
268284

269285
await new Promise((resolve) => setTimeout(resolve, 10));
270286

271-
// Benchmark native bytes input
287+
// Benchmark Uint8Array input
272288
for (let i = 0; i < iterations; i++) {
273289
const start = performance.now();
274290
keccak256(bytesInput);
275291
const end = performance.now();
276-
jsTimes.push(end - start);
292+
bytesTimes.push(end - start);
277293
}
278294

279-
const nativeStats = calculateStats(nativeTimes);
280-
const jsStats = calculateStats(jsTimes);
295+
const stringStats = calculateStats(stringTimes);
296+
const bytesStats = calculateStats(bytesTimes);
281297

282-
const speedupFactor = jsStats.averageTime / nativeStats.averageTime;
283-
const nativeIsFaster = nativeStats.averageTime < jsStats.averageTime;
298+
const speedupFactor = bytesStats.averageTime / stringStats.averageTime;
299+
const stringIsFaster = stringStats.averageTime < bytesStats.averageTime;
284300
const performanceGain =
285-
((jsStats.averageTime - nativeStats.averageTime) / jsStats.averageTime) *
301+
((bytesStats.averageTime - stringStats.averageTime) /
302+
bytesStats.averageTime) *
286303
100;
287304

288305
return {
289-
testName: 'Hex String vs Bytes Input (Native)',
290-
native: nativeStats,
291-
javascript: jsStats,
306+
testName: 'String vs Uint8Array Input',
307+
native: stringStats,
308+
javascript: bytesStats,
292309
comparison: {
293310
speedupFactor,
294-
nativeIsFaster,
311+
nativeIsFaster: stringIsFaster,
295312
performanceGain,
296313
},
297314
};
@@ -303,12 +320,13 @@ export async function runAllKeccak256Benchmarks(): Promise<BenchmarkResult[]> {
303320

304321
const benchmarks = [
305322
{ name: 'Small String', fn: benchmarkSmallString },
323+
{ name: 'Address String', fn: benchmarkAddressString },
306324
{ name: 'Private Key', fn: benchmarkPrivateKey },
307325
{ name: 'Public Key', fn: benchmarkPublicKey },
308326
{ name: 'BIP32 Seed', fn: benchmarkBIP32Seed },
309327
{ name: 'Transaction Data', fn: benchmarkTransactionData },
310328
{ name: 'ETH Address Checksum', fn: benchmarkETHAddressChecksum },
311-
{ name: 'Hex vs Bytes', fn: benchmarkHexVsBytes },
329+
{ name: 'String vs Bytes', fn: benchmarkStringVsBytes },
312330
];
313331

314332
const results: BenchmarkResult[] = [];

0 commit comments

Comments
 (0)