bitmessage-js/lib/platform.browser.js

144 lines
4.6 KiB
JavaScript
Raw Normal View History

2014-12-13 18:56:14 +00:00
/**
2015-01-03 10:29:22 +00:00
* Browser implementation of platform-specific routines.
2014-12-13 18:56:14 +00:00
*/
"use strict";
2015-01-10 13:00:08 +00:00
// `hash.js` is already required by
// `bitmessage -> eccrypto -> elliptic -> hash.js` so it won't add
// additional bytes to the bundle.
// See also: <https://github.com/indutny/elliptic/pull/26>.
var hash = require("hash.js");
2015-01-11 12:00:12 +00:00
// Use only one submodule from `sha.js` here and in worker because it's
// faster. It will add additional bytes to the bundle but not that much
// (~9KB).
2015-01-10 13:00:08 +00:00
var Sha512 = require("sha.js/sha512");
2015-01-07 21:28:13 +00:00
var BN = require("bn.js");
2015-01-10 13:00:08 +00:00
var work = require("webworkify");
var assert = require("./_util").assert;
var cryptoObj = window.crypto || window.msCrypto;
2014-12-18 20:54:27 +00:00
2015-01-16 15:35:40 +00:00
exports.sha1 = function(buf) {
return new Buffer(hash.sha1().update(buf).digest());
};
2014-12-18 16:47:18 +00:00
2014-12-19 12:34:33 +00:00
exports.sha256 = function(buf) {
2015-01-05 13:41:34 +00:00
return new Buffer(hash.sha256().update(buf).digest());
2014-12-19 12:34:33 +00:00
};
2015-01-16 15:35:40 +00:00
exports.sha512 = function(buf) {
return new Sha512().update(buf).digest();
};
exports.ripemd160 = function(buf) {
2015-01-05 13:41:34 +00:00
return new Buffer(hash.ripemd160().update(buf).digest());
};
2014-12-26 17:17:01 +00:00
exports.randomBytes = function(size) {
var arr = new Uint8Array(size);
cryptoObj.getRandomValues(arr);
2014-12-26 17:17:01 +00:00
return new Buffer(arr);
};
2015-01-07 21:28:13 +00:00
2015-01-09 21:36:42 +00:00
// See `platform.js` for comments.
var B80 = new BN("1208925819614629174706176");
2015-01-07 21:28:13 +00:00
exports.getTarget = function(opts) {
var length = new BN(opts.payloadLength);
length.iaddn(opts.payloadLengthExtraBytes);
var denominator = new BN(opts.ttl);
denominator.iaddn(65536);
2015-01-07 21:28:13 +00:00
denominator.imul(length);
denominator.imul(new BN(opts.nonceTrialsPerByte));
var target = parseInt(B80.div(denominator).toString(16), 16);
2015-01-07 21:28:13 +00:00
assert(target <= 9007199254740991, "Unsafe target");
return target;
};
2015-01-09 13:12:23 +00:00
var FAILBACK_POOL_SIZE = 8;
// NOTE(Kagami): We don't use promise shim in Browser implementation
// because it's supported natively in new browsers (see
// <http://caniuse.com/#feat=promises>) and we can use only new browsers
// because of the WebCryptoAPI (see
// <http://caniuse.com/#feat=cryptography>).
2015-01-10 13:03:14 +00:00
exports.pow = function(opts) {
2015-01-09 21:36:42 +00:00
// Try to get CPU cores count otherwise fallback to default value.
// Currenty navigator's concurrency property available in Chrome and
// not available in Firefox; hope default value won't slow down POW
// speed much on systems with 1 or 2 cores. There are core estimator
// libraries exist (see <https://stackoverflow.com/q/3289465>) but
// they are buggy. Ulimately library user could adjust pool size
// manually.
2015-01-09 13:12:23 +00:00
var poolSize = opts.poolSize || navigator.hardwareConcurrency;
poolSize = poolSize || FAILBACK_POOL_SIZE;
var cancel;
2015-01-16 15:35:40 +00:00
var powp = new Promise(function(resolve, reject) {
2015-01-12 18:27:14 +00:00
assert(typeof poolSize === "number", "Bad pool size");
assert(poolSize >= 1, "Pool size is too low");
assert(poolSize <= 1024, "Pool size is too high");
assert(typeof opts.target === "number", "Bad target");
assert(opts.target >= 0, "Target is too low");
assert(opts.target <= 9007199254740991, "Target is too high");
assert(Buffer.isBuffer(opts.initialHash), "Bad initial hash");
assert(opts.initialHash.length === 64, "Bad initial hash");
2015-01-09 13:12:23 +00:00
function terminateAll() {
while (workers.length) {
workers.shift().terminate();
}
}
function onmessage(e) {
terminateAll();
if (e.data >= 0) {
resolve(e.data);
} else {
// It's very unlikely that execution will ever reach this place.
// Currently the only reason why Worker may return value less
// than zero is a 32-bit nonce overflow (see worker
2015-01-09 21:36:42 +00:00
// implementation). It's 4G double hashes.
reject(new Error("uint32_t nonce overflow"));
2015-01-09 13:12:23 +00:00
}
}
function onerror(e) {
// XXX(Kagami): `onerror` events fires in Chrome even after all
// workers were terminated. It doesn't cause wrong behaviour but
// beware that this function may be executed several times.
terminateAll();
reject(e);
}
var workers = [];
var worker;
for (var i = 0; i < poolSize; i++) {
2015-01-10 13:00:08 +00:00
worker = work(require("./worker.browser.js"));
2015-01-09 13:12:23 +00:00
workers.push(worker);
// NOTE(Kagami): There is no race condition here. `onmessage` can
// only be called _after_ this for-loop finishes. See
// <https://stackoverflow.com/a/18192122> for details.
worker.onmessage = onmessage;
worker.onerror = onerror;
worker.postMessage({
num: i,
poolSize: poolSize,
target: opts.target,
initialHash: opts.initialHash,
});
}
cancel = function(e) {
terminateAll();
reject(e);
};
});
2015-01-09 21:36:42 +00:00
// Allow to stop a POW via custom function added to the Promise
2015-01-09 13:12:23 +00:00
// instance.
2015-01-16 15:35:40 +00:00
powp.cancel = cancel;
return powp;
2015-01-09 13:12:23 +00:00
};
2015-01-16 15:35:40 +00:00
2015-02-01 13:10:03 +00:00
exports.Promise = window.Promise;