'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; } }