aboutsummaryrefslogtreecommitdiff
path: root/cmd/anubis/js/proof-of-work.mjs
blob: b4f9c53ea9e47c62cbc5bf57e1d6c60352ca5e03 (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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
export default function process(data, difficulty = 5, threads = (navigator.hardwareConcurrency || 1)) {
  console.debug("fast algo");
  return new Promise((resolve, reject) => {
    let webWorkerURL = URL.createObjectURL(new Blob([
      '(', processTask(), ')()'
    ], { type: 'application/javascript' }));

    const workers = [];

    for (let i = 0; i < threads; i++) {
      let worker = new Worker(webWorkerURL);

      worker.onmessage = (event) => {
        workers.forEach(worker => worker.terminate());
        worker.terminate();
        resolve(event.data);
      };

      worker.onerror = (event) => {
        worker.terminate();
        reject();
      };

      worker.postMessage({
        data,
        difficulty,
        nonce: i,
        threads,
      });

      workers.push(worker);
    }

    URL.revokeObjectURL(webWorkerURL);
  });
}

function processTask() {
  return function () {
    const sha256 = (text) => {
      const encoded = new TextEncoder().encode(text);
      return crypto.subtle.digest("SHA-256", encoded.buffer);
    };

    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;

      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);
          console.log(hash);
          break;
        }

        nonce += threads;
      }

      postMessage({
        hash,
        data,
        difficulty,
        nonce,
      });
    });
  }.toString();
}