80 linhas
1.5 KiB
JavaScript
80 linhas
1.5 KiB
JavaScript
'use strict';
|
|
/*global crypto*/
|
|
|
|
var Hashcash = module.exports;
|
|
|
|
var textEncoder = new TextEncoder();
|
|
Hashcash.solve = async function solveHc(hc) {
|
|
var solution = 0;
|
|
var parts = hc.split(':').slice(0, 6);
|
|
if (parts.length < 6) {
|
|
throw new Error('invalid Hashcash-Challenge: ' + hc);
|
|
}
|
|
|
|
var bits = parseInt(parts[1], 10) || -1;
|
|
if (bits > 10 || bits < 0) {
|
|
throw new Error('bad bit values');
|
|
}
|
|
console.log('bits:', bits);
|
|
hc = parts.join(':') + ':';
|
|
async function next() {
|
|
var answer = hc + int52ToBase64(solution);
|
|
var u8 = textEncoder.encode(answer);
|
|
// REALLY SLOW due to async tasks and C++ context switch
|
|
var hash = await crypto.subtle.digest('SHA-256', u8);
|
|
hash = new Uint8Array(hash);
|
|
if (checkHc(hash, bits)) {
|
|
return answer;
|
|
}
|
|
solution += 1;
|
|
return next();
|
|
}
|
|
|
|
return next();
|
|
};
|
|
|
|
function int52ToBase64(n) {
|
|
var hex = n.toString(16);
|
|
if (hex.length % 2) {
|
|
hex = '0' + hex;
|
|
}
|
|
|
|
var bin = [];
|
|
var i = 0;
|
|
var d;
|
|
var b;
|
|
while (i < hex.length) {
|
|
d = parseInt(hex.slice(i, i + 2), 16);
|
|
b = String.fromCharCode(d);
|
|
bin.push(b);
|
|
i += 2;
|
|
}
|
|
|
|
return btoa(bin.join('')).replace(/=/g, '');
|
|
}
|
|
|
|
function checkHc(hash, bits) {
|
|
var n = Math.floor(bits / 8);
|
|
var m = bits % 8;
|
|
var i;
|
|
if (m > 0) {
|
|
n += 1;
|
|
}
|
|
|
|
for (i = 0; i < n && i < hash.length; i += 1) {
|
|
if (bits > 8) {
|
|
bits -= 8;
|
|
if (0 !== hash[i]) {
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (0 !== hash[i] >> (8 - bits)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|