aboutsummaryrefslogtreecommitdiff
path: root/content/pow-solver.js
blob: 2b9131044493ec16a6b7ea51741bf0da6ddafb54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
 * SPDX-License-Identifier: CC0-1.0
 *
 * Copyright (C) 2025-2026 Woj. Kosior <koszko@koszko.org>
 */

/*
  #+begin_src manifest-jq
  .matches = ["<all_urls>"]
  #+end_src
*/

/* This PoW code is used by multiple content scripts. */

function byteArray2Hex(arrayLike) {
    const byte2Hex = byte => ("0" + byte.toString(16)).slice(-2);
    return [...arrayLike].map(byte2Hex).join("");
}

async function genericSolvePow(randomData, zeroesNeeded, countingSchemes) {
    const zeroBytesNeeded = zeroesNeeded >> 3;
    const finalZeroesNeeded = (zeroesNeeded & 7);
    const finalBitsMask = 255 << (8 - finalZeroesNeeded);

    let solution = null;

    async function powLoop(scheme) {
        const stepArray = Function.bind.call(scheme.stepArray, scheme);

        let inputArray = scheme.makeArray(randomData);
        inputArray = typeof inputArray === 'string' ?
            new TextEncoder().encode(inputArray) : inputArray;

        while (!solution) {
            const digest = new Uint8Array(
                await crypto.subtle.digest("SHA-256", inputArray)
            );
            let success = true;

            for (let idx = 0; success && idx < zeroBytesNeeded; idx++)
                success &&= digest[idx] === 0;

            success &&= (digest[zeroBytesNeeded] & finalBitsMask) === 0;

            if (success) {
                const suffixArray = inputArray.slice(-scheme.suffixLength());
                return solution = [digest, inputArray, suffixArray];
            }

            /* Mutation (side-effects) or new array creation — allow both. */
            inputArray = stepArray(inputArray) || inputArray;
        }

        return solution;
    }

    const startMillis = Date.now();

    /*
     * Under both IceCat 140.3.1-gnu1 and ungoogled-chromium 140.0.7339.207-1
     * (both from Guix) on a Core 2 CPU the best hash/s result was observed when
     * running 2 async loops.  TODO: use workers to leverage hw concurrency.
     */
    const promises = countingSchemes.map(scheme => powLoop(scheme));
    const [digest, inputArray, suffixArray] = await Promise.any(promises);

    return {
        hash: byteArray2Hex(digest),
        inputArray,
        suffixArray,
        elapsedTime: Date.now() - startMillis
    };
}