From fbc44e24f0e72f561da7ff1f70989461aaa5a715 Mon Sep 17 00:00:00 2001 From: Ardis Lu Date: Sat, 29 Jul 2023 15:12:14 -0700 Subject: [PATCH] Use multi-threading --- index.html | 40 ++++++++++++++++++++++++++++------------ worker.js | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 worker.js diff --git a/index.html b/index.html index 77be14b..ae0b310 100644 --- a/index.html +++ b/index.html @@ -89,21 +89,37 @@ document.querySelector('form').addEventListener('submit', e => { e.preventDefault(); - const { factory, bytecode, requirement: requirementString, salt: saltString } = Object.fromEntries(new FormData(e.target)); + const { factory: factoryString, bytecode: bytecodeString, requirement: requirementString, salt: saltString } = Object.fromEntries(new FormData(e.target)); - const hashedBytecode = keccak256(strToArr(bytecode)); // Hash it once and save for efficiency + const factory = strToArr(factoryString); + const hashedBytecode = keccak256(strToArr(bytecodeString)); // Hash it once and save for efficiency const requirement = RegExp(requirementString.slice(1, -1)); - let salt = BigInt(saltString); // Converted to 0n if blank - while (true) { - const paddedSalt = `0x${salt.toString(16).padStart(64, '0')}`; - const addrBytes = new Uint8Array([0xff, ...strToArr(factory), ...strToArr(paddedSalt), ...hashedBytecode]); - const addr = `0x${arrToStr(keccak256(addrBytes)).substring(26)}`; - if (requirement.test(addr)) { - document.querySelector('output').innerHTML = `
${JSON.stringify({ factory, paddedSalt, bytecode, addr }, null, 2)}
`; - break; - } - salt++; + // Use SharedArrayBuffer and Atomics for multi-threading + const saltSab = new SharedArrayBuffer(32); + + // Set initial salt; allows "saving" mining progress by resuming mining from last salt. + // Hardcoded to use the last bytes so that the first bytes may be used for vanity function signatures. + const saltIndex = 3; + new BigUint64Array(saltSab)[saltIndex] = BigInt(saltString); + + const workers = []; + for (let i = 0; i < navigator.hardwareConcurrency; i++) { + const w = new Worker('/worker.js', { type: 'module' }); + workers.push(w); + w.postMessage({ factory, hashedBytecode, requirement, saltSab }); + w.addEventListener('message', e => { + workers.forEach(w => w.terminate()); + + const { newSaltArray, addr } = e.data; + const output = { + factory: factoryString, + bytecode: bytecodeString, + salt: arrToStr(newSaltArray), + addr + } + document.querySelector('output').innerHTML = `
${JSON.stringify(output, null, 2)}
`; + }); } }); diff --git a/worker.js b/worker.js new file mode 100644 index 0000000..1ded0b7 --- /dev/null +++ b/worker.js @@ -0,0 +1,26 @@ +import { keccak256 } from '/keccak256.js'; + +function arrToStr(arr) { + return [...arr].map(v => v.toString(16).padStart(2, '0')).join(''); +} + +addEventListener('message', async e => { + const { factory, hashedBytecode, requirement, saltSab } = e.data; + + // Hardcoded to use the last bytes so that the first bytes may be used for vanity function signatures. + // BigUint64Array is the largest typed array in JS. Max salt is 2n ** 64n - 1n === 18446744073709551615n. + const saltIndex = 3; + const saltArray = new BigUint64Array(saltSab); // Shared buffer + while (true) { + const salt = Atomics.add(saltArray, saltIndex, 1n); + const b64 = saltArray.slice(); // Clones into a new, non-shared buffer + b64[saltIndex] = salt; + const newSaltArray = new Uint8Array(b64.buffer); // Uint8Array required for hashing and presentation + + const addrBytes = new Uint8Array([0xff, ...factory, ...newSaltArray, ...hashedBytecode]); + const addr = `0x${arrToStr(keccak256(addrBytes)).substring(24)}`; + if (requirement.test(addr)) { + postMessage({ newSaltArray, addr }); + } + } +}); -- 2.47.3