diff --git a/Makefile b/Makefile index 5e5885e1..59cbdfc0 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,14 @@ all: - @browserify lib/bcoin.js -o bcoin.browser.js - @uglifyjs --comments '/\*[^\0]+?Copyright[^\0]+?\*/' -o bcoin.min.js bcoin.browser.js + @./node_modules/.bin/browserify lib/bcoin.js -o browser/bcoin.js + +ugly: + @uglifyjs --comments '/\*[^\0]+?Copyright[^\0]+?\*/' -o browser/bcoin.min.js browser/bcoin.js clean: - @rm bcoin.browser.js - @rm bcoin.min.js + @rm browser/bcoin.js + @rm browser/bcoin.min.js test: @npm test -.PHONY: all clean test +.PHONY: all ugly clean test diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index aec06890..e426ede6 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -575,7 +575,6 @@ Block.prototype.inspect = function inspect() { ts: this.ts, bits: this.bits, nonce: this.nonce, - totalTX: this.totalTX, txs: this.txs }; }; diff --git a/lib/bcoin/chainentry.js b/lib/bcoin/chainentry.js index 6d502bc3..e18f0b9d 100644 --- a/lib/bcoin/chainentry.js +++ b/lib/bcoin/chainentry.js @@ -409,7 +409,7 @@ ChainEntry.prototype.toRaw = function toRaw(writer) { p.writeU32(this.bits); p.writeU32(this.nonce); p.writeU32(this.height); - p.writeBytes(this.chainwork.toBuffer('le', 32)); + p.writeBytes(this.chainwork.toArrayLike(Buffer, 'le', 32)); if (!writer) p = p.render(); diff --git a/lib/bcoin/ec.js b/lib/bcoin/ec.js index 20bf06b8..bbce885b 100644 --- a/lib/bcoin/ec.js +++ b/lib/bcoin/ec.js @@ -61,7 +61,7 @@ ec.generatePrivateKey = function generatePrivateKey() { } while (!secp256k1.privateKeyVerify(priv)); } else { key = ec.elliptic.genKeyPair(); - priv = key.getPrivate().toBuffer('be', 32); + priv = key.getPrivate().toArrayLike(Buffer, 'be', 32); } return priv; @@ -93,7 +93,7 @@ ec.publicKeyCreate = function publicKeyCreate(priv, compressed) { ec.random = function random(size) { if (crypto) return crypto.randomBytes(size); - return new Buffer(ec.elliptic.rand(size)); + return new Buffer(elliptic.rand(size)); }; /** diff --git a/lib/bcoin/env.js b/lib/bcoin/env.js index dce71a3c..6bc99d5a 100644 --- a/lib/bcoin/env.js +++ b/lib/bcoin/env.js @@ -189,6 +189,9 @@ function Environment(options) { this.useWorkers = null; this.maxWorkers = null; this.workerTimeout = null; + this.proxyServer = null; + this.workerUri = null; + this.logger = null; this.time = new this.timedata(); @@ -257,9 +260,17 @@ Environment.prototype.set = function set(options) { this.useWorkers = !!options.useWorkers; this.maxWorkers = options.maxWorkers; this.workerTimeout = options.workerTimeout; + this.workerUri = options.workerUri || '/bcoin-worker.js'; + this.proxyServer = options.proxyServer || 'http://localhost:8080'; + this.logger = options.logger; this.network.set(this.networkType); + if (this.isBrowser && this.useWorkers) { + this.useWorkers = typeof utils.global.Worker === 'function' + || typeof utils.global.postMessage === 'function'; + } + if (this.useWorkers) { this.workers = require('./workers'); this.workerPool = new this.workers({ @@ -307,12 +318,15 @@ Environment.prototype.debug = function debug() { var args = Array.prototype.slice.call(arguments); var msg; + if (this.logger) + this.logger.debug(args); + if (this.isBrowser) { if (this.debugLogs) { msg = typeof args[0] !== 'object' ? utils.format(args, false) : args[0]; - console.error(msg); + console.log(msg); } return; } @@ -342,6 +356,9 @@ Environment.prototype.error = function error(err) { if (typeof err === 'string') err = new Error(err); + if (this.logger) + this.logger.error(err); + if (this.isBrowser) { if (this.debugLogs) console.error(err); diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index 41fb4854..36399cc3 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -106,14 +106,16 @@ Fullnode.prototype._init = function _init() { }); // HTTP needs access to the node. - this.http = new bcoin.http.server({ - network: this.network, - node: this, - key: this.options.sslKey, - cert: this.options.sslCert, - port: this.options.httpPort || this.network.rpcPort, - host: this.options.httpHost || '0.0.0.0' - }); + if (!utils.isBrowser) { + this.http = new bcoin.http.server({ + network: this.network, + node: this, + key: this.options.sslKey, + cert: this.options.sslCert, + port: this.options.httpPort || this.network.rpcPort, + host: this.options.httpHost || '0.0.0.0' + }); + } // Bind to errors this.mempool.on('error', function(err) { @@ -132,14 +134,16 @@ Fullnode.prototype._init = function _init() { self.emit('error', err); }); - this.http.on('error', function(err) { - self.emit('error', err); - }); - this.walletdb.on('error', function(err) { self.emit('error', err); }); + if (this.http) { + this.http.on('error', function(err) { + self.emit('error', err); + }); + } + this.on('tx', function(tx) { self.walletdb.addTX(tx, function(err) { if (err) @@ -235,7 +239,11 @@ Fullnode.prototype._init = function _init() { next(); }); }, - this.http.open.bind(this.http) + function(next) { + if (!self.http) + return next(); + self.http.open(next); + } ], load); }; @@ -341,9 +349,16 @@ Fullnode.prototype.open = function open(callback) { Fullnode.prototype.close = Fullnode.prototype.destroy = function destroy(callback) { + var self = this; + this.wallet.destroy(); + utils.serial([ - this.http.close.bind(this.http), + function(next) { + if (!self.http) + return next(); + self.http.close(next); + }, this.walletdb.close.bind(this.walletdb), this.pool.close.bind(this.pool), this.miner.close.bind(this.miner), diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 4351b475..32db17b0 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -566,7 +566,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { privateKey = left .add(new bn(this.privateKey)) .mod(ec.elliptic.curve.n) - .toBuffer('be', 32); + .toArrayLike(Buffer, 'be', 32); if (!this.fingerPrint) this.fingerPrint = utils.ripesha(this.publicKey).slice(0, 4); diff --git a/lib/bcoin/http/base.js b/lib/bcoin/http/base.js index b4185744..40949517 100644 --- a/lib/bcoin/http/base.js +++ b/lib/bcoin/http/base.js @@ -71,6 +71,9 @@ HTTPBase.prototype._initIO = function _initIO() { var self = this; var IOServer; + if (!this.options.sockets) + return; + try { IOServer = require('socket.io'); } catch (e) { @@ -93,8 +96,8 @@ HTTPBase.prototype._initRouter = function _initRouter() { var self = this; this.server.on('request', function(req, res) { - function _send(code, msg) { - send(res, code, msg); + function _send(code, msg, type) { + send(res, code, msg, type); } function done(err) { @@ -347,17 +350,36 @@ HTTPBase.prototype.close = function close(callback) { * Helpers */ -function send(res, code, msg) { +function send(res, code, msg, type) { + var len; + if (!msg) msg = { error: 'No message.' }; try { res.statusCode = code; - if (typeof msg === 'object') { - res.setHeader('Content-Type', 'application/json; charset=utf-8'); + + if (msg && typeof msg === 'object' && !Buffer.isBuffer(msg)) { msg = JSON.stringify(msg, null, 2) + '\n'; + type = 'json'; } - res.setHeader('Content-Length', Buffer.byteLength(msg) + ''); + + if (type === 'html') + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + else if (type === 'text') + res.setHeader('Content-Type', 'text/plain; charset=utf-8'); + else if (type === 'json') + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + else if (type === 'js') + res.setHeader('Content-Type', 'application/javascript; charset=utf-8'); + else if (type === 'binary') + res.setHeader('Content-Type', 'application/octet-stream; charset=utf-8'); + + len = typeof msg === 'string' + ? Buffer.byteLength(msg, 'utf8') + : msg.length; + + res.setHeader('Content-Length', len + ''); res.write(msg); res.end(); } catch (e) { diff --git a/lib/bcoin/http/server.js b/lib/bcoin/http/server.js index 1206f512..0142db3c 100644 --- a/lib/bcoin/http/server.js +++ b/lib/bcoin/http/server.js @@ -24,6 +24,9 @@ var assert = utils.assert; */ function HTTPServer(options) { + if (!(this instanceof HTTPServer)) + return new HTTPServer(options); + if (!options) options = {}; @@ -38,6 +41,8 @@ function HTTPServer(options) { this.pool = this.node.pool; this.loaded = false; + options.sockets = true; + this.server = new HTTPBase(options); this._init(); diff --git a/lib/bcoin/ip.js b/lib/bcoin/ip.js index d0b0425c..81db4cb3 100644 --- a/lib/bcoin/ip.js +++ b/lib/bcoin/ip.js @@ -105,7 +105,7 @@ exports.isMapped = function isMapped(ip) { * @returns {Buffer} */ -exports.toBuffer = function toArray(ip) { +exports.toBuffer = function toBuffer(ip) { var out; if (Buffer.isBuffer(ip)) { @@ -114,7 +114,7 @@ exports.toBuffer = function toArray(ip) { } if (!ip) - return toArray('0.0.0.0'); + return toBuffer('0.0.0.0'); assert(typeof ip === 'string'); assert(exports.version(ip) !== -1); diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index 77102bee..807b5735 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -353,7 +353,7 @@ function MinerBlock(options) { this.workerPool = options.workerPool; this.tip = options.tip; this.height = options.tip.height + 1; - this.target = utils.fromCompact(options.target).toBuffer('le', 32); + this.target = utils.fromCompact(options.target).toArrayLike(Buffer, 'le', 32); this.extraNonce = new bn(0); this.iterations = 0; this.coinbaseFlags = options.coinbaseFlags; diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 9943eb9b..0f7f8a82 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -156,6 +156,7 @@ Peer.prototype._init = function init() { self.ts = utils.now(); self.connected = true; self.emit('connect'); + self._onconnect(); }); this.socket.once('error', function(err) { @@ -180,6 +181,10 @@ Peer.prototype._init = function init() { self.sendReject(null, 'malformed', 'error parsing message', 10); self._error(err, true); }); +}; + +Peer.prototype._onconnect = function _onconnect() { + var self = this; this.request('verack', function callee(err) { if (err) { @@ -277,15 +282,18 @@ Peer.prototype._init = function init() { Peer.prototype.createSocket = function createSocket(port, host) { var hostname = IP.hostname(host, port); - var socket, net; + var socket, proxy, net; if (this._createSocket) { socket = this._createSocket(port, host); } else { - if (bcoin.isBrowser) - throw new Error('Please include a `createSocket` callback.'); - net = require('n' + 'et'); - socket = net.connect(port, host); + if (bcoin.isBrowser) { + proxy = require('../../browser/proxysocket'); + socket = proxy.connect(bcoin.proxyServer, port, host); + } else { + net = require('n' + 'et'); + socket = net.connect(port, host); + } } bcoin.debug('Connecting to %s.', hostname); diff --git a/lib/bcoin/profiler.js b/lib/bcoin/profiler.js index 64223012..8d0911e3 100644 --- a/lib/bcoin/profiler.js +++ b/lib/bcoin/profiler.js @@ -35,7 +35,7 @@ function ensure() { function Profile(name) { if (v8profiler && bcoin.profile) { - name = 'profile-' + (name ? name + '-' : '') + Date.now(); + name = 'profile-' + (name ? name + '-' : '') + utils.ms(); bcoin.debug('Starting CPU profile: %s', name); v8profiler.startProfiling(name, true); this.name = name; @@ -123,7 +123,7 @@ Profile.prototype.save = function save(callback) { function Snapshot(name) { if (v8profiler && bcoin.profile) { - name = 'snapshot-' + (name ? name + '-' : '') + Date.now(); + name = 'snapshot-' + (name ? name + '-' : '') + utils.ms(); bcoin.debug('Taking heap snapshot: %s', name); this.snapshot = v8profiler.takeSnapshot(name); this.name = name; @@ -247,7 +247,7 @@ profiler.snapshot = function snapshot(name, callback) { name = null; } - if (bcoin.debugLogs) { + if (bcoin.debugLogs && process.memoryUsage) { mem = process.memoryUsage(); bcoin.debug('Memory: rss=%dmb, js-heap=%d/%dmb native-heap=%dmb', utils.mb(mem.rss), @@ -257,7 +257,7 @@ profiler.snapshot = function snapshot(name, callback) { } if (!v8profiler || !bcoin.profile) - return callback ? utils.nextTick(callback) : null; + return utils.asyncify(callback)(); snapshot = new Snapshot(name); snapshot.save(callback); diff --git a/lib/bcoin/spvnode.js b/lib/bcoin/spvnode.js index 29631657..dd8d6074 100644 --- a/lib/bcoin/spvnode.js +++ b/lib/bcoin/spvnode.js @@ -71,14 +71,16 @@ SPVNode.prototype._init = function _init() { verify: true }); - this.http = new bcoin.http.server({ - network: this.network, - node: this, - key: this.options.sslKey, - cert: this.options.sslCert, - port: this.options.httpPort || this.network.rpcPort, - host: '0.0.0.0' - }); + if (!utils.isBrowser) { + this.http = new bcoin.http.server({ + network: this.network, + node: this, + key: this.options.sslKey, + cert: this.options.sslCert, + port: this.options.httpPort || this.network.rpcPort, + host: '0.0.0.0' + }); + } // Bind to errors this.pool.on('error', function(err) { @@ -89,14 +91,16 @@ SPVNode.prototype._init = function _init() { self.emit('error', err); }); - this.http.on('error', function(err) { - self.emit('error', err); - }); - this.walletdb.on('error', function(err) { self.emit('error', err); }); + if (this.http) { + this.http.on('error', function(err) { + self.emit('error', err); + }); + } + this.on('tx', function(tx) { self.walletdb.addTX(tx, function(err) { if (err) @@ -208,7 +212,11 @@ SPVNode.prototype._init = function _init() { self.chain.reset(height, next); }); }, - this.http.open.bind(this.http) + function(next) { + if (!self.http) + return next(); + self.http.open(next); + } ], load); }; @@ -291,9 +299,16 @@ SPVNode.prototype.open = function open(callback) { SPVNode.prototype.close = SPVNode.prototype.destroy = function destroy(callback) { + var self = this; + this.wallet.destroy(); + utils.parallel([ - this.http.close.bind(this.http), + function(next) { + if (!self.http) + return next(); + self.http.close(next); + }, this.walletdb.close.bind(this.walletdb), this.pool.close.bind(this.pool), this.chain.close.bind(this.chain) diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 631344a0..bdb6b365 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -548,6 +548,9 @@ utils.equal = function equal(a, b) { * @param {Function} callback */ +if (utils.isBrowser) + require('../../browser/setimmediate'); + if (typeof setImmediate === 'function') { utils.nextTick = setImmediate; } else if (!utils.isBrowser) { @@ -907,7 +910,7 @@ utils.error = function error() { msg = typeof args[0] !== 'object' ? utils.format(args, false) : args[0]; - console.error(msg); + console.log(msg); return; } diff --git a/lib/bcoin/worker.js b/lib/bcoin/worker.js index 72dc9ae1..a29aef35 100644 --- a/lib/bcoin/worker.js +++ b/lib/bcoin/worker.js @@ -17,6 +17,7 @@ if (typeof importScripts !== 'undefined') { env = JSON.parse(event.data); + bcoin.set({ useWorkers: true }); bcoin.network.set(env.BCOIN_WORKER_NETWORK); bcoin.workers.listen(+env.BCOIN_WORKER_ID, { debug: +env.BCOIN_WORKER_DEBUG === 1 @@ -25,6 +26,7 @@ if (typeof importScripts !== 'undefined') { } else { env = process.env; bcoin = require('./env'); + bcoin.set({ useWorkers: true }); bcoin.network.set(env.BCOIN_WORKER_NETWORK); bcoin.workers.listen(+env.BCOIN_WORKER_ID, { debug: +env.BCOIN_WORKER_DEBUG === 1 diff --git a/lib/bcoin/workers.js b/lib/bcoin/workers.js index bd53edbc..95c02f49 100644 --- a/lib/bcoin/workers.js +++ b/lib/bcoin/workers.js @@ -824,7 +824,7 @@ Framer.item = function _item(item, p) { item.toRaw(p); } else if (bn.isBN(item)) { p.writeU8(50); - p.writeVarBytes(item.toBuffer()); + p.writeVarBytes(item.toArrayLike(Buffer)); } else if (Buffer.isBuffer(item)) { p.writeU8(4); p.writeVarBytes(item); @@ -1001,7 +1001,7 @@ function getCores() { var os; if (utils.isBrowser) - return 4; + return 2; os = require('o' + 's'); diff --git a/package.json b/package.json index 60172024..f0967b64 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "browserify": "13.0.0", "hash.js": "1.0.3", "jsdoc": "3.4.0", - "level-js": "2.2.3", + "level-js": "2.2.4", "mocha": "2.4.5", "uglify-js": "2.6.1", "v8-profiler": "5.6.1"