* fix(js): use pure JS SHA256 library, refactor Closes #458 Additionally, I made a horrifying discovery: Firefox seems to actively hinder performance if you are using more than one Worker per page. It does not spread the load out across cores like I expected. Instead it seems to make that one Worker thrash and have to constantly context switch, which caused a lot of slowdown. The benchmarks in #155 continue to be the best contribution ever made to Anubis. What clued me into there being a problem here was the fact that the "slow" algorithm was faster than the "fast" algorithm on my laptop. This made no intuitive sense to me so I dug further. Either way I think this is a Firefox bug at its core, but for now we have to work around it by doing the hacky terrible thing that I hate. I also swapped the SHA256 operations to @aws-crypto/sha256-js on the advice of a trusted cryptography expert. I don't know what performance differences this makes, but I'm getting 150-225 kilohashes per second, which is pretty dang good. Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(js): apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(js): use fast algo for fast worker Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
69 lines
No EOL
1.8 KiB
JavaScript
69 lines
No EOL
1.8 KiB
JavaScript
import { Sha256 } from '@aws-crypto/sha256-js';
|
|
|
|
const sha256 = (text) => {
|
|
const hash = new Sha256();
|
|
hash.update(text);
|
|
return hash.digest();
|
|
};
|
|
|
|
function uint8ArrayToHexString(arr) {
|
|
return Array.from(arr)
|
|
.map((c) => c.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
}
|
|
|
|
addEventListener('message', async (event) => {
|
|
let data = event.data.data;
|
|
let difficulty = event.data.difficulty;
|
|
let hash;
|
|
let nonce = event.data.nonce;
|
|
let threads = event.data.threads;
|
|
|
|
const threadId = nonce;
|
|
|
|
while (true) {
|
|
const currentHash = await sha256(data + nonce);
|
|
const thisHash = new Uint8Array(currentHash);
|
|
let valid = true;
|
|
|
|
for (let j = 0; j < difficulty; j++) {
|
|
const byteIndex = Math.floor(j / 2); // which byte we are looking at
|
|
const nibbleIndex = j % 2; // which nibble in the byte we are looking at (0 is high, 1 is low)
|
|
|
|
let nibble = (thisHash[byteIndex] >> (nibbleIndex === 0 ? 4 : 0)) & 0x0F; // Get the nibble
|
|
|
|
if (nibble !== 0) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid) {
|
|
hash = uint8ArrayToHexString(thisHash);
|
|
break;
|
|
}
|
|
|
|
const oldNonce = nonce;
|
|
nonce += threads;
|
|
|
|
// send a progress update every 1024 iterations. since each thread checks
|
|
// separate values, one simple way to do this is by bit masking the
|
|
// nonce for multiples of 1024. unfortunately, if the number of threads
|
|
// is not prime, only some of the threads will be sending the status
|
|
// update and they will get behind the others. this is slightly more
|
|
// complicated but ensures an even distribution between threads.
|
|
if (
|
|
nonce > oldNonce | 1023 && // we've wrapped past 1024
|
|
(nonce >> 10) % threads === threadId // and it's our turn
|
|
) {
|
|
postMessage(nonce);
|
|
}
|
|
}
|
|
|
|
postMessage({
|
|
hash,
|
|
data,
|
|
difficulty,
|
|
nonce,
|
|
});
|
|
}); |