diff --git a/lib/workers/child-browser.js b/lib/workers/child-browser.js index 8e10f85d..7b340b14 100644 --- a/lib/workers/child-browser.js +++ b/lib/workers/child-browser.js @@ -10,86 +10,89 @@ const assert = require('assert'); const EventEmitter = require('events'); /** + * Child * Represents a child process. * @alias module:workers.Child - * @constructor + * @extends EventEmitter * @ignore - * @param {String} file */ -function Child(file) { - if (!(this instanceof Child)) - return new Child(file); +class Child extends EventEmitter { + /** + * Represents a child process. + * @constructor + * @param {String} file + */ - EventEmitter.call(this); + constructor(file) { + super(); - this.init(file); -} - -Object.setPrototypeOf(Child.prototype, EventEmitter.prototype); - -/** - * Test whether child process support is available. - * @returns {Boolean} - */ - -Child.hasSupport = function hasSupport() { - return typeof global.postMessage === 'function'; -}; - -/** - * Initialize child process. Bind to events. - * @private - * @param {String} file - */ - -Child.prototype.init = function init(file) { - this.child = new global.Worker(file); - - this.child.onerror = (event) => { - this.emit('error', new Error('Child error.')); - this.emit('exit', 1, null); - }; - - this.child.onmessage = (event) => { - let data; - if (typeof event.data === 'string') { - data = Buffer.from(event.data, 'hex'); - assert(data.length === event.data.length / 2); - } else { - assert(event.data && typeof event.data === 'object'); - assert(event.data.data && typeof event.data.data.length === 'number'); - data = event.data.data; - data.__proto__ = Buffer.prototype; - } - this.emit('data', data); - }; -}; - -/** - * Send data to child process. - * @param {Buffer} data - * @returns {Boolean} - */ - -Child.prototype.write = function write(data) { - if (this.child.postMessage.length === 2) { - data.__proto__ = Uint8Array.prototype; - this.child.postMessage({ data }, [data]); - } else { - this.child.postMessage(data.toString('hex')); + this.init(file); } - return true; -}; -/** - * Destroy the child process. - */ + /** + * Test whether child process support is available. + * @returns {Boolean} + */ -Child.prototype.destroy = function destroy() { - this.child.terminate(); - this.emit('exit', 15 | 0x80, 'SIGTERM'); -}; + static hasSupport() { + return typeof global.postMessage === 'function'; + } + + /** + * Initialize child process. Bind to events. + * @private + * @param {String} file + */ + + init(file) { + this.child = new global.Worker(file); + + this.child.onerror = (event) => { + this.emit('error', new Error('Child error.')); + this.emit('exit', 1, null); + }; + + this.child.onmessage = (event) => { + let data; + if (typeof event.data === 'string') { + data = Buffer.from(event.data, 'hex'); + assert(data.length === event.data.length / 2); + } else { + assert(event.data && typeof event.data === 'object'); + assert(event.data.data && typeof event.data.data.length === 'number'); + data = event.data.data; + data.__proto__ = Buffer.prototype; + } + this.emit('data', data); + }; + } + + /** + * Send data to child process. + * @param {Buffer} data + * @returns {Boolean} + */ + + write(data) { + if (this.child.postMessage.length === 2) { + data.__proto__ = Uint8Array.prototype; + this.child.postMessage({ data }, [data]); + } else { + this.child.postMessage(data.toString('hex')); + } + return true; + } + + /** + * Destroy the child process. + */ + + destroy() { + this.child.terminate(); + this.emit('exit', 15 | 0x80, 'SIGTERM'); + } +} /* * Expose diff --git a/lib/workers/child.js b/lib/workers/child.js index f6df990d..f4627aa4 100644 --- a/lib/workers/child.js +++ b/lib/workers/child.js @@ -14,97 +14,101 @@ const children = new Set(); let exitBound = false; /** + * Child * Represents a child process. * @alias module:workers.Child - * @constructor - * @param {String} file + * @extends EventEmitter + * @ignore */ -function Child(file) { - if (!(this instanceof Child)) - return new Child(file); +class Child extends EventEmitter { + /** + * Represents a child process. + * @constructor + * @param {String} file + */ - EventEmitter.call(this); + constructor(file) { + super(); - bindExit(); - children.add(this); + bindExit(); + children.add(this); - this.init(file); + this.init(file); + } + + /** + * Test whether child process support is available. + * @returns {Boolean} + */ + + static hasSupport() { + return true; + } + + /** + * Initialize child process (node.js). + * @private + * @param {String} file + */ + + init(file) { + const bin = process.argv[0]; + const filename = path.resolve(__dirname, file); + const options = { stdio: 'pipe', env: process.env }; + + this.child = cp.spawn(bin, [filename], options); + + this.child.unref(); + this.child.stdin.unref(); + this.child.stdout.unref(); + this.child.stderr.unref(); + + this.child.on('error', (err) => { + this.emit('error', err); + }); + + this.child.once('exit', (code, signal) => { + children.delete(this); + this.emit('exit', code == null ? -1 : code, signal); + }); + + this.child.stdin.on('error', (err) => { + this.emit('error', err); + }); + + this.child.stdout.on('error', (err) => { + this.emit('error', err); + }); + + this.child.stderr.on('error', (err) => { + this.emit('error', err); + }); + + this.child.stdout.on('data', (data) => { + this.emit('data', data); + }); + } + + /** + * Send data to child process. + * @param {Buffer} data + * @returns {Boolean} + */ + + write(data) { + return this.child.stdin.write(data); + } + + /** + * Destroy the child process. + */ + + destroy() { + this.child.kill('SIGTERM'); + } } -Object.setPrototypeOf(Child.prototype, EventEmitter.prototype); - -/** - * Test whether child process support is available. - * @returns {Boolean} - */ - -Child.hasSupport = function hasSupport() { - return true; -}; - -/** - * Initialize child process (node.js). - * @private - * @param {String} file - */ - -Child.prototype.init = function init(file) { - const bin = process.argv[0]; - const filename = path.resolve(__dirname, file); - const options = { stdio: 'pipe', env: process.env }; - - this.child = cp.spawn(bin, [filename], options); - - this.child.unref(); - this.child.stdin.unref(); - this.child.stdout.unref(); - this.child.stderr.unref(); - - this.child.on('error', (err) => { - this.emit('error', err); - }); - - this.child.once('exit', (code, signal) => { - children.delete(this); - this.emit('exit', code == null ? -1 : code, signal); - }); - - this.child.stdin.on('error', (err) => { - this.emit('error', err); - }); - - this.child.stdout.on('error', (err) => { - this.emit('error', err); - }); - - this.child.stderr.on('error', (err) => { - this.emit('error', err); - }); - - this.child.stdout.on('data', (data) => { - this.emit('data', data); - }); -}; - -/** - * Send data to child process. - * @param {Buffer} data - * @returns {Boolean} - */ - -Child.prototype.write = function write(data) { - return this.child.stdin.write(data); -}; - -/** - * Destroy the child process. - */ - -Child.prototype.destroy = function destroy() { - this.child.kill('SIGTERM'); -}; - /** * Cleanup all child processes. * @private diff --git a/lib/workers/framer.js b/lib/workers/framer.js index fb2f8d56..75725284 100644 --- a/lib/workers/framer.js +++ b/lib/workers/framer.js @@ -12,32 +12,35 @@ const bio = require('bufio'); /** * Framer * @alias module:workers.Framer - * @constructor */ -function Framer() { - if (!(this instanceof Framer)) - return new Framer(); +class Framer { + /** + * Create a framer. + * @constructor + */ + + constructor() {} + + packet(payload) { + const size = 10 + payload.getSize(); + const bw = bio.write(size); + + bw.writeU32(payload.id); + bw.writeU8(payload.cmd); + bw.seek(4); + + payload.toWriter(bw); + + bw.writeU8(0x0a); + + const msg = bw.render(); + msg.writeUInt32LE(msg.length - 10, 5, true); + + return msg; + } } -Framer.prototype.packet = function packet(payload) { - const size = 10 + payload.getSize(); - const bw = bio.write(size); - - bw.writeU32(payload.id); - bw.writeU8(payload.cmd); - bw.seek(4); - - payload.toWriter(bw); - - bw.writeU8(0x0a); - - const msg = bw.render(); - msg.writeUInt32LE(msg.length - 10, 5, true); - - return msg; -}; - /* * Expose */ diff --git a/lib/workers/master.js b/lib/workers/master.js index 229f2c00..885b3bb6 100644 --- a/lib/workers/master.js +++ b/lib/workers/master.js @@ -18,174 +18,177 @@ const packets = require('./packets'); const Parent = require('./parent'); /** + * Master * Represents the master process. * @alias module:workers.Master - * @constructor + * @extends EventEmitter */ -function Master() { - if (!(this instanceof Master)) - return new Master(); +class Master extends EventEmitter { + /** + * Create the master process. + * @constructor + */ - EventEmitter.call(this); + constructor() { + super(); - this.parent = new Parent(); - this.framer = new Framer(); - this.parser = new Parser(); - this.listening = false; - this.color = false; + this.parent = new Parent(); + this.framer = new Framer(); + this.parser = new Parser(); + this.listening = false; + this.color = false; - this.init(); -} - -Object.setPrototypeOf(Master.prototype, EventEmitter.prototype); - -/** - * Initialize master. Bind events. - * @private - */ - -Master.prototype.init = function init() { - this.parent.on('data', (data) => { - this.parser.feed(data); - }); - - this.parent.on('error', (err) => { - this.emit('error', err); - }); - - this.parent.on('exception', (err) => { - this.send(new packets.ErrorPacket(err)); - setTimeout(() => this.destroy(), 1000); - }); - - this.parser.on('error', (err) => { - this.emit('error', err); - }); - - this.parser.on('packet', (packet) => { - this.emit('packet', packet); - }); -}; - -/** - * Set environment. - * @param {Object} env - */ - -Master.prototype.setEnv = function setEnv(env) { - this.color = env.BCOIN_WORKER_ISTTY === '1'; - this.set(env.BCOIN_WORKER_NETWORK); -}; - -/** - * Set primary network. - * @param {NetworkType|Network} network - */ - -Master.prototype.set = function set(network) { - return Network.set(network); -}; - -/** - * Send data to worker. - * @param {Buffer} data - * @returns {Boolean} - */ - -Master.prototype.write = function write(data) { - return this.parent.write(data); -}; - -/** - * Frame and send a packet. - * @param {Packet} packet - * @returns {Boolean} - */ - -Master.prototype.send = function send(packet) { - return this.write(this.framer.packet(packet)); -}; - -/** - * Emit an event on the worker side. - * @param {String} event - * @param {...Object} arg - * @returns {Boolean} - */ - -Master.prototype.sendEvent = function sendEvent(...items) { - return this.send(new packets.EventPacket(items)); -}; - -/** - * Destroy the worker. - */ - -Master.prototype.destroy = function destroy() { - return this.parent.destroy(); -}; - -/** - * Write a message to stdout in the master process. - * @param {Object|String} obj - * @param {...String} args - */ - -Master.prototype.log = function log() { - const text = format.apply(null, arguments); - this.send(new packets.LogPacket(text)); -}; - -/** - * Listen for messages from master process (only if worker). - */ - -Master.prototype.listen = function listen() { - assert(!this.listening, 'Already listening.'); - - this.listening = true; - - this.on('error', (err) => { - this.send(new packets.ErrorPacket(err)); - }); - - this.on('packet', (packet) => { - try { - this.handlePacket(packet); - } catch (e) { - this.emit('error', e); - } - }); -}; - -/** - * Handle packet. - * @private - * @param {Packet} - */ - -Master.prototype.handlePacket = function handlePacket(packet) { - let result; - - switch (packet.cmd) { - case packets.types.ENV: - this.setEnv(packet.env); - break; - case packets.types.EVENT: - this.emit('event', packet.items); - this.emit(...packet.items); - break; - case packets.types.ERROR: - this.emit('error', packet.error); - break; - default: - result = jobs.execute(packet); - result.id = packet.id; - this.send(result); - break; + this.init(); } -}; + + /** + * Initialize master. Bind events. + * @private + */ + + init() { + this.parent.on('data', (data) => { + this.parser.feed(data); + }); + + this.parent.on('error', (err) => { + this.emit('error', err); + }); + + this.parent.on('exception', (err) => { + this.send(new packets.ErrorPacket(err)); + setTimeout(() => this.destroy(), 1000); + }); + + this.parser.on('error', (err) => { + this.emit('error', err); + }); + + this.parser.on('packet', (packet) => { + this.emit('packet', packet); + }); + } + + /** + * Set environment. + * @param {Object} env + */ + + setEnv(env) { + this.color = env.BCOIN_WORKER_ISTTY === '1'; + this.set(env.BCOIN_WORKER_NETWORK); + } + + /** + * Set primary network. + * @param {NetworkType|Network} network + */ + + set(network) { + return Network.set(network); + } + + /** + * Send data to worker. + * @param {Buffer} data + * @returns {Boolean} + */ + + write(data) { + return this.parent.write(data); + } + + /** + * Frame and send a packet. + * @param {Packet} packet + * @returns {Boolean} + */ + + send(packet) { + return this.write(this.framer.packet(packet)); + } + + /** + * Emit an event on the worker side. + * @param {String} event + * @param {...Object} arg + * @returns {Boolean} + */ + + sendEvent(...items) { + return this.send(new packets.EventPacket(items)); + } + + /** + * Destroy the worker. + */ + + destroy() { + return this.parent.destroy(); + } + + /** + * Write a message to stdout in the master process. + * @param {Object|String} obj + * @param {...String} args + */ + + log() { + const text = format.apply(null, arguments); + this.send(new packets.LogPacket(text)); + } + + /** + * Listen for messages from master process (only if worker). + */ + + listen() { + assert(!this.listening, 'Already listening.'); + + this.listening = true; + + this.on('error', (err) => { + this.send(new packets.ErrorPacket(err)); + }); + + this.on('packet', (packet) => { + try { + this.handlePacket(packet); + } catch (e) { + this.emit('error', e); + } + }); + } + + /** + * Handle packet. + * @private + * @param {Packet} + */ + + handlePacket(packet) { + let result; + + switch (packet.cmd) { + case packets.types.ENV: + this.setEnv(packet.env); + break; + case packets.types.EVENT: + this.emit('event', packet.items); + this.emit(...packet.items); + break; + case packets.types.ERROR: + this.emit('error', packet.error); + break; + default: + result = jobs.execute(packet); + result.id = packet.id; + this.send(result); + break; + } + } +} /* * Expose diff --git a/lib/workers/packets.js b/lib/workers/packets.js index 86788a2d..02223c54 100644 --- a/lib/workers/packets.js +++ b/lib/workers/packets.js @@ -52,1036 +52,992 @@ const packetTypes = { /** * Packet - * @constructor */ -function Packet() { - this.id = ++Packet.id >>> 0; +class Packet { + constructor() { + this.id = ++Packet.id >>> 0; + this.cmd = -1; + } + + getSize() { + throw new Error('Abstract method.'); + } + + toWriter() { + throw new Error('Abstract method.'); + } + + fromRaw() { + throw new Error('Abstract method.'); + } + + static fromRaw() { + throw new Error('Abstract method.'); + } } Packet.id = 0; -Packet.prototype.cmd = -1; - -Packet.prototype.getSize = function getSize() { - throw new Error('Abstract method.'); -}; - -Packet.prototype.toWriter = function toWriter() { - throw new Error('Abstract method.'); -}; - -Packet.prototype.fromRaw = function fromRaw() { - throw new Error('Abstract method.'); -}; - -Packet.fromRaw = function fromRaw() { - throw new Error('Abstract method.'); -}; - /** * EnvPacket - * @constructor */ -function EnvPacket(env) { - Packet.call(this); - this.env = env || {}; - this.json = JSON.stringify(this.env); +class EnvPacket extends Packet { + constructor(env) { + super(); + this.cmd = packetTypes.ENV; + this.env = env || {}; + this.json = JSON.stringify(this.env); + } + + getSize() { + return encoding.sizeVarString(this.json, 'utf8'); + } + + toWriter(bw) { + bw.writeVarString(this.json, 'utf8'); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.json = br.readVarString('utf8'); + this.env = JSON.parse(this.json); + return this; + } + + static fromRaw(data) { + return new EnvPacket().fromRaw(data); + } } -Object.setPrototypeOf(EnvPacket.prototype, Packet.prototype); - -EnvPacket.prototype.cmd = packetTypes.ENV; - -EnvPacket.prototype.getSize = function getSize() { - return encoding.sizeVarString(this.json, 'utf8'); -}; - -EnvPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarString(this.json, 'utf8'); - return bw; -}; - -EnvPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.json = br.readVarString('utf8'); - this.env = JSON.parse(this.json); - return this; -}; - -EnvPacket.fromRaw = function fromRaw(data) { - return new EnvPacket().fromRaw(data); -}; - /** * EventPacket - * @constructor */ -function EventPacket(items) { - Packet.call(this); - this.items = items || []; - this.json = JSON.stringify(this.items); +class EventPacket extends Packet { + constructor(items) { + super(); + this.cmd = packetTypes.EVENT; + this.items = items || []; + this.json = JSON.stringify(this.items); + } + + getSize() { + return encoding.sizeVarString(this.json, 'utf8'); + } + + toWriter(bw) { + bw.writeVarString(this.json, 'utf8'); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.json = br.readVarString('utf8'); + this.items = JSON.parse(this.json); + return this; + } + + static fromRaw(data) { + return new EventPacket().fromRaw(data); + } } -Object.setPrototypeOf(EventPacket.prototype, Packet.prototype); - -EventPacket.prototype.cmd = packetTypes.EVENT; - -EventPacket.prototype.getSize = function getSize() { - return encoding.sizeVarString(this.json, 'utf8'); -}; - -EventPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarString(this.json, 'utf8'); - return bw; -}; - -EventPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.json = br.readVarString('utf8'); - this.items = JSON.parse(this.json); - return this; -}; - -EventPacket.fromRaw = function fromRaw(data) { - return new EventPacket().fromRaw(data); -}; - /** * LogPacket - * @constructor */ -function LogPacket(text) { - Packet.call(this); - this.text = text || ''; +class LogPacket extends Packet { + constructor(text) { + super(); + this.cmd = packetTypes.LOG; + this.text = text || ''; + } + + getSize() { + return encoding.sizeVarString(this.text, 'utf8'); + } + + toWriter(bw) { + bw.writeVarString(this.text, 'utf8'); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.text = br.readVarString('utf8'); + return this; + } + + static fromRaw(data) { + return new LogPacket().fromRaw(data); + } } -Object.setPrototypeOf(LogPacket.prototype, Packet.prototype); - -LogPacket.prototype.cmd = packetTypes.LOG; - -LogPacket.prototype.getSize = function getSize() { - return encoding.sizeVarString(this.text, 'utf8'); -}; - -LogPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarString(this.text, 'utf8'); - return bw; -}; - -LogPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.text = br.readVarString('utf8'); - return this; -}; - -LogPacket.fromRaw = function fromRaw(data) { - return new LogPacket().fromRaw(data); -}; - /** * ErrorPacket - * @constructor */ -function ErrorPacket(error) { - Packet.call(this); - this.error = error || new Error(); +class ErrorPacket extends Packet { + constructor(error) { + super(); + this.cmd = packetTypes.ERROR; + this.error = error || new Error(); + } + + getSize() { + const err = this.error; + + let size = 0; + + size += encoding.sizeVarString(stringify(err.message), 'utf8'); + size += encoding.sizeVarString(stringify(err.stack), 'utf8'); + size += encoding.sizeVarString(stringify(err.type), 'utf8'); + + switch (typeof err.code) { + case 'number': + size += 1; + size += 4; + break; + case 'string': + size += 1; + size += encoding.sizeVarString(err.code, 'utf8'); + break; + default: + size += 1; + break; + } + + return size; + } + + toWriter(bw) { + const err = this.error; + + bw.writeVarString(stringify(err.message), 'utf8'); + bw.writeVarString(stringify(err.stack), 'utf8'); + bw.writeVarString(stringify(err.type), 'utf8'); + + switch (typeof err.code) { + case 'number': + bw.writeU8(2); + bw.writeI32(err.code); + break; + case 'string': + bw.writeU8(1); + bw.writeVarString(err.code, 'utf8'); + break; + default: + bw.writeU8(0); + break; + } + + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + const err = this.error; + + err.message = br.readVarString('utf8'); + err.stack = br.readVarString('utf8'); + err.type = br.readVarString('utf8'); + + switch (br.readU8()) { + case 2: + err.code = br.readI32(); + break; + case 1: + err.code = br.readVarString('utf8'); + break; + default: + err.code = null; + break; + } + + return this; + } + + static fromRaw(data) { + return new ErrorPacket().fromRaw(data); + } } -Object.setPrototypeOf(ErrorPacket.prototype, Packet.prototype); - -ErrorPacket.prototype.cmd = packetTypes.ERROR; - -ErrorPacket.prototype.getSize = function getSize() { - const err = this.error; - - let size = 0; - - size += encoding.sizeVarString(stringify(err.message), 'utf8'); - size += encoding.sizeVarString(stringify(err.stack), 'utf8'); - size += encoding.sizeVarString(stringify(err.type), 'utf8'); - - switch (typeof err.code) { - case 'number': - size += 1; - size += 4; - break; - case 'string': - size += 1; - size += encoding.sizeVarString(err.code, 'utf8'); - break; - default: - size += 1; - break; - } - - return size; -}; - -ErrorPacket.prototype.toWriter = function toWriter(bw) { - const err = this.error; - - bw.writeVarString(stringify(err.message), 'utf8'); - bw.writeVarString(stringify(err.stack), 'utf8'); - bw.writeVarString(stringify(err.type), 'utf8'); - - switch (typeof err.code) { - case 'number': - bw.writeU8(2); - bw.writeI32(err.code); - break; - case 'string': - bw.writeU8(1); - bw.writeVarString(err.code, 'utf8'); - break; - default: - bw.writeU8(0); - break; - } - - return bw; -}; - -ErrorPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - const err = this.error; - - err.message = br.readVarString('utf8'); - err.stack = br.readVarString('utf8'); - err.type = br.readVarString('utf8'); - - switch (br.readU8()) { - case 2: - err.code = br.readI32(); - break; - case 1: - err.code = br.readVarString('utf8'); - break; - default: - err.code = null; - break; - } - - return this; -}; - -ErrorPacket.fromRaw = function fromRaw(data) { - return new ErrorPacket().fromRaw(data); -}; - /** * ErrorResultPacket - * @constructor */ -function ErrorResultPacket(error) { - ErrorPacket.call(this, error); +class ErrorResultPacket extends ErrorPacket { + constructor(error) { + super(error); + this.cmd = packetTypes.ERRORRESULT; + } + + static fromRaw(data) { + return new ErrorResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(ErrorResultPacket.prototype, ErrorPacket.prototype); - -ErrorResultPacket.prototype.cmd = packetTypes.ERRORRESULT; - -ErrorResultPacket.fromRaw = function fromRaw(data) { - return new ErrorResultPacket().fromRaw(data); -}; - /** * CheckPacket - * @constructor */ -function CheckPacket(tx, view, flags) { - Packet.call(this); - this.tx = tx || null; - this.view = view || null; - this.flags = flags != null ? flags : null; +class CheckPacket extends Packet { + constructor(tx, view, flags) { + super(); + this.cmd = packetTypes.CHECK; + this.tx = tx || null; + this.view = view || null; + this.flags = flags != null ? flags : null; + } + + getSize() { + return this.tx.getSize() + this.view.getSize(this.tx) + 4; + } + + toWriter(bw) { + this.tx.toWriter(bw); + this.view.toWriter(bw, this.tx); + bw.writeI32(this.flags != null ? this.flags : -1); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + + this.tx = TX.fromReader(br); + this.view = CoinView.fromReader(br, this.tx); + this.flags = br.readI32(); + + if (this.flags === -1) + this.flags = null; + + return this; + } + + static fromRaw(data) { + return new CheckPacket().fromRaw(data); + } } -Object.setPrototypeOf(CheckPacket.prototype, Packet.prototype); - -CheckPacket.prototype.cmd = packetTypes.CHECK; - -CheckPacket.prototype.getSize = function getSize() { - return this.tx.getSize() + this.view.getSize(this.tx) + 4; -}; - -CheckPacket.prototype.toWriter = function toWriter(bw) { - this.tx.toWriter(bw); - this.view.toWriter(bw, this.tx); - bw.writeI32(this.flags != null ? this.flags : -1); - return bw; -}; - -CheckPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - - this.tx = TX.fromReader(br); - this.view = CoinView.fromReader(br, this.tx); - this.flags = br.readI32(); - - if (this.flags === -1) - this.flags = null; - - return this; -}; - -CheckPacket.fromRaw = function fromRaw(data) { - return new CheckPacket().fromRaw(data); -}; - /** * CheckResultPacket - * @constructor */ -function CheckResultPacket(error) { - Packet.call(this); - this.error = error || null; +class CheckResultPacket extends Packet { + constructor(error) { + super(); + this.cmd = packetTypes.CHECKRESULT; + this.error = error || null; + } + + getSize() { + const err = this.error; + + let size = 0; + + if (!err) { + size += 1; + return size; + } + + size += 1; + size += encoding.sizeVarString(stringify(err.message), 'utf8'); + size += encoding.sizeVarString(stringify(err.stack), 'utf8'); + size += encoding.sizeVarString(stringify(err.code), 'utf8'); + size += 1; + size += 4; + + return size; + } + + toWriter(bw) { + const err = this.error; + + if (!err) { + bw.writeU8(0); + return bw; + } + + bw.writeU8(1); + bw.writeVarString(stringify(err.message), 'utf8'); + bw.writeVarString(stringify(err.stack), 'utf8'); + bw.writeVarString(stringify(err.code), 'utf8'); + bw.writeU8(err.op === -1 ? 0xff : err.op); + bw.writeU32(err.ip === -1 ? 0xffffffff : err.ip); + + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + + if (br.readU8() === 0) + return this; + + const err = new ScriptError(''); + + err.message = br.readVarString('utf8'); + err.stack = br.readVarString('utf8'); + err.code = br.readVarString('utf8'); + err.op = br.readU8(); + err.ip = br.readU32(); + + if (err.op === 0xff) + err.op = -1; + + if (err.ip === 0xffffffff) + err.ip = -1; + + this.error = err; + + return this; + } + + static fromRaw(data) { + return new CheckResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(CheckResultPacket.prototype, Packet.prototype); +/** + * SignPacket + */ -CheckResultPacket.prototype.cmd = packetTypes.CHECKRESULT; +class SignPacket extends Packet { + constructor(tx, rings, type) { + super(); + this.cmd = packetTypes.SIGN; + this.tx = tx || null; + this.rings = rings || []; + this.type = type != null ? type : 1; + } -CheckResultPacket.prototype.getSize = function getSize() { - const err = this.error; + getSize() { + let size = 0; - let size = 0; + size += this.tx.getSize(); + size += this.tx.view.getSize(this.tx); + size += encoding.sizeVarint(this.rings.length); - if (!err) { + for (const ring of this.rings) + size += ring.getSize(); + + size += 1; + + return size; + } + + toWriter(bw) { + this.tx.toWriter(bw); + this.tx.view.toWriter(bw, this.tx); + + bw.writeVarint(this.rings.length); + + for (const ring of this.rings) + ring.toWriter(bw); + + bw.writeU8(this.type); + + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + + this.tx = MTX.fromReader(br); + this.tx.view.fromReader(br, this.tx); + + const count = br.readVarint(); + + for (let i = 0; i < count; i++) { + const ring = KeyRing.fromReader(br); + this.rings.push(ring); + } + + this.type = br.readU8(); + + return this; + } + + static fromRaw(data) { + return new SignPacket().fromRaw(data); + } +} + +/** + * SignResultPacket + */ + +class SignResultPacket extends Packet { + constructor(total, witness, script) { + super(); + this.cmd = packetTypes.SIGNRESULT; + this.total = total || 0; + this.script = script || []; + this.witness = witness || []; + } + + fromTX(tx, total) { + this.total = total; + + for (const input of tx.inputs) { + this.script.push(input.script); + this.witness.push(input.witness); + } + + return this; + } + + static fromTX(tx, total) { + return new SignResultPacket().fromTX(tx, total); + } + + getSize() { + let size = 0; + + size += encoding.sizeVarint(this.total); + size += encoding.sizeVarint(this.script.length); + + for (let i = 0; i < this.script.length; i++) { + const script = this.script[i]; + const witness = this.witness[i]; + size += script.getVarSize(); + size += witness.getVarSize(); + } + + return size; + } + + toWriter(bw) { + assert(this.script.length === this.witness.length); + + bw.writeVarint(this.total); + bw.writeVarint(this.script.length); + + for (let i = 0; i < this.script.length; i++) { + this.script[i].toWriter(bw); + this.witness[i].toWriter(bw); + } + + return bw; + } + + inject(tx) { + assert(this.script.length === tx.inputs.length); + assert(this.witness.length === tx.inputs.length); + + for (let i = 0; i < tx.inputs.length; i++) { + const input = tx.inputs[i]; + input.script = this.script[i]; + input.witness = this.witness[i]; + } + } + + fromRaw(data) { + const br = bio.read(data, true); + + this.total = br.readVarint(); + + const count = br.readVarint(); + + for (let i = 0; i < count; i++) { + this.script.push(Script.fromReader(br)); + this.witness.push(Witness.fromReader(br)); + } + + return this; + } + + static fromRaw(data) { + return new SignResultPacket().fromRaw(data); + } +} + +/** + * CheckInputPacket + */ + +class CheckInputPacket extends Packet { + constructor(tx, index, coin, flags) { + super(); + this.cmd = packetTypes.CHECKINPUT; + this.tx = tx || null; + this.index = index; + this.coin = coin || null; + this.flags = flags != null ? flags : null; + } + + getSize() { + let size = 0; + size += this.tx.getSize(); + size += encoding.sizeVarint(this.index); + size += encoding.sizeVarint(this.coin.value); + size += this.coin.script.getVarSize(); + size += 4; + return size; + } + + toWriter(bw) { + this.tx.toWriter(bw); + bw.writeVarint(this.index); + bw.writeVarint(this.coin.value); + this.coin.script.toWriter(bw); + bw.writeI32(this.flags != null ? this.flags : -1); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + + this.tx = TX.fromReader(br); + this.index = br.readVarint(); + + this.coin = new Output(); + this.coin.value = br.readVarint(); + this.coin.script.fromReader(br); + + this.flags = br.readI32(); + + if (this.flags === -1) + this.flags = null; + + return this; + } + + static fromRaw(data) { + return new CheckInputPacket().fromRaw(data); + } +} + +/** + * CheckInputResultPacket + */ + +class CheckInputResultPacket extends CheckResultPacket { + constructor(error) { + super(error); + this.cmd = packetTypes.CHECKINPUTRESULT; + } + + static fromRaw(data) { + return new CheckInputResultPacket().fromRaw(data); + } +} + +/** + * SignInputPacket + */ + +class SignInputPacket extends Packet { + constructor(tx, index, coin, ring, type) { + super(); + this.cmd = packetTypes.SIGNINPUT; + this.tx = tx || null; + this.index = index; + this.coin = coin || null; + this.ring = ring || null; + this.type = type != null ? type : 1; + } + + getSize() { + let size = 0; + size += this.tx.getSize(); + size += encoding.sizeVarint(this.index); + size += encoding.sizeVarint(this.coin.value); + size += this.coin.script.getVarSize(); + size += this.ring.getSize(); size += 1; return size; } - size += 1; - size += encoding.sizeVarString(stringify(err.message), 'utf8'); - size += encoding.sizeVarString(stringify(err.stack), 'utf8'); - size += encoding.sizeVarString(stringify(err.code), 'utf8'); - size += 1; - size += 4; - - return size; -}; - -CheckResultPacket.prototype.toWriter = function toWriter(bw) { - const err = this.error; - - if (!err) { - bw.writeU8(0); + toWriter(bw) { + this.tx.toWriter(bw); + bw.writeVarint(this.index); + bw.writeVarint(this.coin.value); + this.coin.script.toWriter(bw); + this.ring.toWriter(bw); + bw.writeU8(this.type); return bw; } - bw.writeU8(1); - bw.writeVarString(stringify(err.message), 'utf8'); - bw.writeVarString(stringify(err.stack), 'utf8'); - bw.writeVarString(stringify(err.code), 'utf8'); - bw.writeU8(err.op === -1 ? 0xff : err.op); - bw.writeU32(err.ip === -1 ? 0xffffffff : err.ip); + fromRaw(data) { + const br = bio.read(data, true); - return bw; -}; + this.tx = MTX.fromReader(br); + this.index = br.readVarint(); -CheckResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); + this.coin = new Output(); + this.coin.value = br.readVarint(); + this.coin.script.fromReader(br); + + this.ring = KeyRing.fromReader(br); + this.type = br.readU8(); - if (br.readU8() === 0) return this; + } - const err = new ScriptError(''); - - err.message = br.readVarString('utf8'); - err.stack = br.readVarString('utf8'); - err.code = br.readVarString('utf8'); - err.op = br.readU8(); - err.ip = br.readU32(); - - if (err.op === 0xff) - err.op = -1; - - if (err.ip === 0xffffffff) - err.ip = -1; - - this.error = err; - - return this; -}; - -CheckResultPacket.fromRaw = function fromRaw(data) { - return new CheckResultPacket().fromRaw(data); -}; - -/** - * SignPacket - * @constructor - */ - -function SignPacket(tx, rings, type) { - Packet.call(this); - this.tx = tx || null; - this.rings = rings || []; - this.type = type != null ? type : 1; + static fromRaw(data) { + return new SignInputPacket().fromRaw(data); + } } -Object.setPrototypeOf(SignPacket.prototype, Packet.prototype); - -SignPacket.prototype.cmd = packetTypes.SIGN; - -SignPacket.prototype.getSize = function getSize() { - let size = 0; - - size += this.tx.getSize(); - size += this.tx.view.getSize(this.tx); - size += encoding.sizeVarint(this.rings.length); - - for (const ring of this.rings) - size += ring.getSize(); - - size += 1; - - return size; -}; - -SignPacket.prototype.toWriter = function toWriter(bw) { - this.tx.toWriter(bw); - this.tx.view.toWriter(bw, this.tx); - - bw.writeVarint(this.rings.length); - - for (const ring of this.rings) - ring.toWriter(bw); - - bw.writeU8(this.type); - - return bw; -}; - -SignPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - - this.tx = MTX.fromReader(br); - this.tx.view.fromReader(br, this.tx); - - const count = br.readVarint(); - - for (let i = 0; i < count; i++) { - const ring = KeyRing.fromReader(br); - this.rings.push(ring); - } - - this.type = br.readU8(); - - return this; -}; - -SignPacket.fromRaw = function fromRaw(data) { - return new SignPacket().fromRaw(data); -}; - -/** - * SignResultPacket - * @constructor - */ - -function SignResultPacket(total, witness, script) { - Packet.call(this); - this.total = total || 0; - this.script = script || []; - this.witness = witness || []; -} - -Object.setPrototypeOf(SignResultPacket.prototype, Packet.prototype); - -SignResultPacket.prototype.cmd = packetTypes.SIGNRESULT; - -SignResultPacket.prototype.fromTX = function fromTX(tx, total) { - this.total = total; - - for (const input of tx.inputs) { - this.script.push(input.script); - this.witness.push(input.witness); - } - - return this; -}; - -SignResultPacket.fromTX = function fromTX(tx, total) { - return new SignResultPacket().fromTX(tx, total); -}; - -SignResultPacket.prototype.getSize = function getSize() { - let size = 0; - - size += encoding.sizeVarint(this.total); - size += encoding.sizeVarint(this.script.length); - - for (let i = 0; i < this.script.length; i++) { - const script = this.script[i]; - const witness = this.witness[i]; - size += script.getVarSize(); - size += witness.getVarSize(); - } - - return size; -}; - -SignResultPacket.prototype.toWriter = function toWriter(bw) { - assert(this.script.length === this.witness.length); - - bw.writeVarint(this.total); - bw.writeVarint(this.script.length); - - for (let i = 0; i < this.script.length; i++) { - this.script[i].toWriter(bw); - this.witness[i].toWriter(bw); - } - - return bw; -}; - -SignResultPacket.prototype.inject = function inject(tx) { - assert(this.script.length === tx.inputs.length); - assert(this.witness.length === tx.inputs.length); - - for (let i = 0; i < tx.inputs.length; i++) { - const input = tx.inputs[i]; - input.script = this.script[i]; - input.witness = this.witness[i]; - } -}; - -SignResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - - this.total = br.readVarint(); - - const count = br.readVarint(); - - for (let i = 0; i < count; i++) { - this.script.push(Script.fromReader(br)); - this.witness.push(Witness.fromReader(br)); - } - - return this; -}; - -SignResultPacket.fromRaw = function fromRaw(data) { - return new SignResultPacket().fromRaw(data); -}; - -/** - * CheckInputPacket - * @constructor - */ - -function CheckInputPacket(tx, index, coin, flags) { - Packet.call(this); - this.tx = tx || null; - this.index = index; - this.coin = coin || null; - this.flags = flags != null ? flags : null; -} - -Object.setPrototypeOf(CheckInputPacket.prototype, Packet.prototype); - -CheckInputPacket.prototype.cmd = packetTypes.CHECKINPUT; - -CheckInputPacket.prototype.getSize = function getSize() { - let size = 0; - size += this.tx.getSize(); - size += encoding.sizeVarint(this.index); - size += encoding.sizeVarint(this.coin.value); - size += this.coin.script.getVarSize(); - size += 4; - return size; -}; - -CheckInputPacket.prototype.toWriter = function toWriter(bw) { - this.tx.toWriter(bw); - bw.writeVarint(this.index); - bw.writeVarint(this.coin.value); - this.coin.script.toWriter(bw); - bw.writeI32(this.flags != null ? this.flags : -1); - return bw; -}; - -CheckInputPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - - this.tx = TX.fromReader(br); - this.index = br.readVarint(); - - this.coin = new Output(); - this.coin.value = br.readVarint(); - this.coin.script.fromReader(br); - - this.flags = br.readI32(); - - if (this.flags === -1) - this.flags = null; - - return this; -}; - -CheckInputPacket.fromRaw = function fromRaw(data) { - return new CheckInputPacket().fromRaw(data); -}; - -/** - * CheckInputResultPacket - * @constructor - */ - -function CheckInputResultPacket(error) { - CheckResultPacket.call(this, error); -} - -Object.setPrototypeOf( - CheckInputResultPacket.prototype, - CheckResultPacket.prototype); - -CheckInputResultPacket.prototype.cmd = packetTypes.CHECKINPUTRESULT; - -CheckInputResultPacket.fromRaw = function fromRaw(data) { - return new CheckInputResultPacket().fromRaw(data); -}; - -/** - * SignInputPacket - * @constructor - */ - -function SignInputPacket(tx, index, coin, ring, type) { - Packet.call(this); - this.tx = tx || null; - this.index = index; - this.coin = coin || null; - this.ring = ring || null; - this.type = type != null ? type : 1; -} - -Object.setPrototypeOf(SignInputPacket.prototype, Packet.prototype); - -SignInputPacket.prototype.cmd = packetTypes.SIGNINPUT; - -SignInputPacket.prototype.getSize = function getSize() { - let size = 0; - size += this.tx.getSize(); - size += encoding.sizeVarint(this.index); - size += encoding.sizeVarint(this.coin.value); - size += this.coin.script.getVarSize(); - size += this.ring.getSize(); - size += 1; - return size; -}; - -SignInputPacket.prototype.toWriter = function toWriter(bw) { - this.tx.toWriter(bw); - bw.writeVarint(this.index); - bw.writeVarint(this.coin.value); - this.coin.script.toWriter(bw); - this.ring.toWriter(bw); - bw.writeU8(this.type); - return bw; -}; - -SignInputPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - - this.tx = MTX.fromReader(br); - this.index = br.readVarint(); - - this.coin = new Output(); - this.coin.value = br.readVarint(); - this.coin.script.fromReader(br); - - this.ring = KeyRing.fromReader(br); - this.type = br.readU8(); - - return this; -}; - -SignInputPacket.fromRaw = function fromRaw(data) { - return new SignInputPacket().fromRaw(data); -}; - /** * SignInputResultPacket - * @constructor */ -function SignInputResultPacket(value, witness, script) { - Packet.call(this); - this.value = value || false; - this.script = script || null; - this.witness = witness || null; +class SignInputResultPacket extends Packet { + constructor(value, witness, script) { + super(); + this.cmd = packetTypes.SIGNINPUTRESULT; + this.value = value || false; + this.script = script || null; + this.witness = witness || null; + } + + fromTX(tx, i, value) { + const input = tx.inputs[i]; + + assert(input); + + this.value = value; + this.script = input.script; + this.witness = input.witness; + + return this; + } + + static fromTX(tx, i, value) { + return new SignInputResultPacket().fromTX(tx, i, value); + } + + getSize() { + return 1 + this.script.getVarSize() + this.witness.getVarSize(); + } + + toWriter(bw) { + bw.writeU8(this.value ? 1 : 0); + this.script.toWriter(bw); + this.witness.toWriter(bw); + return bw; + } + + inject(tx, i) { + const input = tx.inputs[i]; + assert(input); + input.script = this.script; + input.witness = this.witness; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.value = br.readU8() === 1; + this.script = Script.fromReader(br); + this.witness = Witness.fromReader(br); + return this; + } + + static fromRaw(data) { + return new SignInputResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(SignInputResultPacket.prototype, Packet.prototype); - -SignInputResultPacket.prototype.cmd = packetTypes.SIGNINPUTRESULT; - -SignInputResultPacket.prototype.fromTX = function fromTX(tx, i, value) { - const input = tx.inputs[i]; - - assert(input); - - this.value = value; - this.script = input.script; - this.witness = input.witness; - - return this; -}; - -SignInputResultPacket.fromTX = function fromTX(tx, i, value) { - return new SignInputResultPacket().fromTX(tx, i, value); -}; - -SignInputResultPacket.prototype.getSize = function getSize() { - return 1 + this.script.getVarSize() + this.witness.getVarSize(); -}; - -SignInputResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeU8(this.value ? 1 : 0); - this.script.toWriter(bw); - this.witness.toWriter(bw); - return bw; -}; - -SignInputResultPacket.prototype.inject = function inject(tx, i) { - const input = tx.inputs[i]; - assert(input); - input.script = this.script; - input.witness = this.witness; -}; - -SignInputResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.value = br.readU8() === 1; - this.script = Script.fromReader(br); - this.witness = Witness.fromReader(br); - return this; -}; - -SignInputResultPacket.fromRaw = function fromRaw(data) { - return new SignInputResultPacket().fromRaw(data); -}; - /** * ECVerifyPacket - * @constructor */ -function ECVerifyPacket(msg, sig, key) { - Packet.call(this); - this.msg = msg || null; - this.sig = sig || null; - this.key = key || null; +class ECVerifyPacket extends Packet { + constructor(msg, sig, key) { + super(); + this.cmd = packetTypes.ECVERIFY; + this.msg = msg || null; + this.sig = sig || null; + this.key = key || null; + } + + getSize() { + let size = 0; + size += encoding.sizeVarBytes(this.msg); + size += encoding.sizeVarBytes(this.sig); + size += encoding.sizeVarBytes(this.key); + return size; + } + + toWriter(bw) { + bw.writeVarBytes(this.msg); + bw.writeVarBytes(this.sig); + bw.writeVarBytes(this.key); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.msg = br.readVarBytes(); + this.sig = br.readVarBytes(); + this.key = br.readVarBytes(); + return this; + } + + static fromRaw(data) { + return new ECVerifyPacket().fromRaw(data); + } } -Object.setPrototypeOf(ECVerifyPacket.prototype, Packet.prototype); - -ECVerifyPacket.prototype.cmd = packetTypes.ECVERIFY; - -ECVerifyPacket.prototype.getSize = function getSize() { - let size = 0; - size += encoding.sizeVarBytes(this.msg); - size += encoding.sizeVarBytes(this.sig); - size += encoding.sizeVarBytes(this.key); - return size; -}; - -ECVerifyPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarBytes(this.msg); - bw.writeVarBytes(this.sig); - bw.writeVarBytes(this.key); - return bw; -}; - -ECVerifyPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.msg = br.readVarBytes(); - this.sig = br.readVarBytes(); - this.key = br.readVarBytes(); - return this; -}; - -ECVerifyPacket.fromRaw = function fromRaw(data) { - return new ECVerifyPacket().fromRaw(data); -}; - /** * ECVerifyResultPacket - * @constructor */ -function ECVerifyResultPacket(value) { - Packet.call(this); - this.value = value; +class ECVerifyResultPacket extends Packet { + constructor(value) { + super(); + this.cmd = packetTypes.ECVERIFYRESULT; + this.value = value; + } + + getSize() { + return 1; + } + + toWriter(bw) { + bw.writeU8(this.value ? 1 : 0); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.value = br.readU8() === 1; + return this; + } + + static fromRaw(data) { + return new ECVerifyResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(ECVerifyResultPacket.prototype, Packet.prototype); - -ECVerifyResultPacket.prototype.cmd = packetTypes.ECVERIFYRESULT; - -ECVerifyResultPacket.prototype.getSize = function getSize() { - return 1; -}; - -ECVerifyResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeU8(this.value ? 1 : 0); - return bw; -}; - -ECVerifyResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.value = br.readU8() === 1; - return this; -}; - -ECVerifyResultPacket.fromRaw = function fromRaw(data) { - return new ECVerifyResultPacket().fromRaw(data); -}; - /** * ECSignPacket - * @constructor */ -function ECSignPacket(msg, key) { - Packet.call(this); - this.msg = msg || null; - this.key = key || null; +class ECSignPacket extends Packet { + constructor(msg, key) { + super(); + this.cmd = packetTypes.ECSIGN; + this.msg = msg || null; + this.key = key || null; + } + + getSize() { + let size = 0; + size += encoding.sizeVarBytes(this.msg); + size += encoding.sizeVarBytes(this.key); + return size; + } + + toWriter(bw) { + bw.writeVarBytes(this.msg); + bw.writeVarBytes(this.key); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.msg = br.readVarBytes(); + this.key = br.readVarBytes(); + return this; + } + + static fromRaw(data) { + return new ECSignPacket().fromRaw(data); + } } -Object.setPrototypeOf(ECSignPacket.prototype, Packet.prototype); - -ECSignPacket.prototype.cmd = packetTypes.ECSIGN; - -ECSignPacket.prototype.getSize = function getSize() { - let size = 0; - size += encoding.sizeVarBytes(this.msg); - size += encoding.sizeVarBytes(this.key); - return size; -}; - -ECSignPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarBytes(this.msg); - bw.writeVarBytes(this.key); - return bw; -}; - -ECSignPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.msg = br.readVarBytes(); - this.key = br.readVarBytes(); - return this; -}; - -ECSignPacket.fromRaw = function fromRaw(data) { - return new ECSignPacket().fromRaw(data); -}; - /** * ECSignResultPacket - * @constructor */ -function ECSignResultPacket(sig) { - Packet.call(this); - this.sig = sig; +class ECSignResultPacket extends Packet { + constructor(sig) { + super(); + this.cmd = packetTypes.ECSIGNRESULT; + this.sig = sig; + } + + getSize() { + return encoding.sizeVarBytes(this.sig); + } + + toWriter(bw) { + bw.writeVarBytes(this.sig); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.sig = br.readVarBytes(); + return this; + } + + static fromRaw(data) { + return new ECSignResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(ECSignResultPacket.prototype, Packet.prototype); - -ECSignResultPacket.prototype.cmd = packetTypes.ECSIGNRESULT; - -ECSignResultPacket.prototype.getSize = function getSize() { - return encoding.sizeVarBytes(this.sig); -}; - -ECSignResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarBytes(this.sig); - return bw; -}; - -ECSignResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.sig = br.readVarBytes(); - return this; -}; - -ECSignResultPacket.fromRaw = function fromRaw(data) { - return new ECSignResultPacket().fromRaw(data); -}; - /** * MinePacket - * @constructor */ -function MinePacket(data, target, min, max) { - Packet.call(this); - this.data = data || null; - this.target = target || null; - this.min = min != null ? min : -1; - this.max = max != null ? max : -1; +class MinePacket extends Packet { + constructor(data, target, min, max) { + super(); + this.cmd = packetTypes.MINE; + this.data = data || null; + this.target = target || null; + this.min = min != null ? min : -1; + this.max = max != null ? max : -1; + } + + getSize() { + return 120; + } + + toWriter(bw) { + bw.writeBytes(this.data); + bw.writeBytes(this.target); + bw.writeU32(this.min); + bw.writeU32(this.max); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.data = br.readBytes(80); + this.target = br.readBytes(32); + this.min = br.readU32(); + this.max = br.readU32(); + return this; + } + + static fromRaw(data) { + return new MinePacket().fromRaw(data); + } } -Object.setPrototypeOf(MinePacket.prototype, Packet.prototype); - -MinePacket.prototype.cmd = packetTypes.MINE; - -MinePacket.prototype.getSize = function getSize() { - return 120; -}; - -MinePacket.prototype.toWriter = function toWriter(bw) { - bw.writeBytes(this.data); - bw.writeBytes(this.target); - bw.writeU32(this.min); - bw.writeU32(this.max); - return bw; -}; - -MinePacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.data = br.readBytes(80); - this.target = br.readBytes(32); - this.min = br.readU32(); - this.max = br.readU32(); - return this; -}; - -MinePacket.fromRaw = function fromRaw(data) { - return new MinePacket().fromRaw(data); -}; - /** * MineResultPacket - * @constructor */ -function MineResultPacket(nonce) { - Packet.call(this); - this.nonce = nonce != null ? nonce : -1; +class MineResultPacket extends Packet { + constructor(nonce) { + super(); + this.cmd = packetTypes.MINERESULT; + this.nonce = nonce != null ? nonce : -1; + } + + getSize() { + return 5; + } + + toWriter(bw) { + bw.writeU8(this.nonce !== -1 ? 1 : 0); + bw.writeU32(this.nonce); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.nonce = -1; + if (br.readU8() === 1) + this.nonce = br.readU32(); + return this; + } + + static fromRaw(data) { + return new MineResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(MineResultPacket.prototype, Packet.prototype); - -MineResultPacket.prototype.cmd = packetTypes.MINERESULT; - -MineResultPacket.prototype.getSize = function getSize() { - return 5; -}; - -MineResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeU8(this.nonce !== -1 ? 1 : 0); - bw.writeU32(this.nonce); - return bw; -}; - -MineResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.nonce = -1; - if (br.readU8() === 1) - this.nonce = br.readU32(); - return this; -}; - -MineResultPacket.fromRaw = function fromRaw(data) { - return new MineResultPacket().fromRaw(data); -}; - /** * ScryptPacket - * @constructor */ -function ScryptPacket(passwd, salt, N, r, p, len) { - Packet.call(this); - this.passwd = passwd || null; - this.salt = salt || null; - this.N = N != null ? N : -1; - this.r = r != null ? r : -1; - this.p = p != null ? p : -1; - this.len = len != null ? len : -1; +class ScryptPacket extends Packet { + constructor(passwd, salt, N, r, p, len) { + super(); + this.cmd = packetTypes.SCRYPT; + this.passwd = passwd || null; + this.salt = salt || null; + this.N = N != null ? N : -1; + this.r = r != null ? r : -1; + this.p = p != null ? p : -1; + this.len = len != null ? len : -1; + } + + getSize() { + let size = 0; + size += encoding.sizeVarBytes(this.passwd); + size += encoding.sizeVarBytes(this.salt); + size += 16; + return size; + } + + toWriter(bw) { + bw.writeVarBytes(this.passwd); + bw.writeVarBytes(this.salt); + bw.writeU32(this.N); + bw.writeU32(this.r); + bw.writeU32(this.p); + bw.writeU32(this.len); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.passwd = br.readVarBytes(); + this.salt = br.readVarBytes(); + this.N = br.readU32(); + this.r = br.readU32(); + this.p = br.readU32(); + this.len = br.readU32(); + return this; + } + + static fromRaw(data) { + return new ScryptPacket().fromRaw(data); + } } -Object.setPrototypeOf(ScryptPacket.prototype, Packet.prototype); - -ScryptPacket.prototype.cmd = packetTypes.SCRYPT; - -ScryptPacket.prototype.getSize = function getSize() { - let size = 0; - size += encoding.sizeVarBytes(this.passwd); - size += encoding.sizeVarBytes(this.salt); - size += 16; - return size; -}; - -ScryptPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarBytes(this.passwd); - bw.writeVarBytes(this.salt); - bw.writeU32(this.N); - bw.writeU32(this.r); - bw.writeU32(this.p); - bw.writeU32(this.len); - return bw; -}; - -ScryptPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.passwd = br.readVarBytes(); - this.salt = br.readVarBytes(); - this.N = br.readU32(); - this.r = br.readU32(); - this.p = br.readU32(); - this.len = br.readU32(); - return this; -}; - -ScryptPacket.fromRaw = function fromRaw(data) { - return new ScryptPacket().fromRaw(data); -}; - /** * ScryptResultPacket - * @constructor */ -function ScryptResultPacket(key) { - Packet.call(this); - this.key = key || null; +class ScryptResultPacket extends Packet { + constructor(key) { + super(); + this.cmd = packetTypes.SCRYPTRESULT; + this.key = key || null; + } + + getSize() { + return encoding.sizeVarBytes(this.key); + } + + toWriter(bw) { + bw.writeVarBytes(this.key); + return bw; + } + + fromRaw(data) { + const br = bio.read(data, true); + this.key = br.readVarBytes(); + return this; + } + + static fromRaw(data) { + return new ScryptResultPacket().fromRaw(data); + } } -Object.setPrototypeOf(ScryptResultPacket.prototype, Packet.prototype); - -ScryptResultPacket.prototype.cmd = packetTypes.SCRYPTRESULT; - -ScryptResultPacket.prototype.getSize = function getSize() { - return encoding.sizeVarBytes(this.key); -}; - -ScryptResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeVarBytes(this.key); - return bw; -}; - -ScryptResultPacket.prototype.fromRaw = function fromRaw(data) { - const br = bio.read(data, true); - this.key = br.readVarBytes(); - return this; -}; - -ScryptResultPacket.fromRaw = function fromRaw(data) { - return new ScryptResultPacket().fromRaw(data); -}; - /* * Helpers */ diff --git a/lib/workers/parent-browser.js b/lib/workers/parent-browser.js index 2eb865e7..7db99f1f 100644 --- a/lib/workers/parent-browser.js +++ b/lib/workers/parent-browser.js @@ -10,71 +10,74 @@ const assert = require('assert'); const EventEmitter = require('events'); /** + * Parent * Represents the parent process. * @alias module:workers.Parent - * @constructor + * @extends EventEmitter * @ignore */ -function Parent() { - if (!(this instanceof Parent)) - return new Parent(); +class Parent extends EventEmitter { + /** + * Create the parent process. + * @constructor + */ - EventEmitter.call(this); + constructor() { + super(); - this.init(); -} - -Object.setPrototypeOf(Parent.prototype, EventEmitter.prototype); - -/** - * Initialize master (web workers). - * @private - */ - -Parent.prototype.init = function init() { - global.onerror = (event) => { - this.emit('error', new Error('Worker error.')); - }; - - global.onmessage = (event) => { - let data; - if (typeof event.data === 'string') { - data = Buffer.from(event.data, 'hex'); - assert(data.length === event.data.length / 2); - } else { - assert(event.data && typeof event.data === 'object'); - assert(event.data.data && typeof event.data.data.length === 'number'); - data = event.data.data; - data.__proto__ = Buffer.prototype; - } - this.emit('data', data); - }; -}; - -/** - * Send data to parent process. - * @param {Buffer} data - * @returns {Boolean} - */ - -Parent.prototype.write = function write(data) { - if (global.postMessage.length === 2) { - data.__proto__ = Uint8Array.prototype; - global.postMessage({ data }, [data]); - } else { - global.postMessage(data.toString('hex')); + this.init(); } - return true; -}; -/** - * Destroy the parent process. - */ + /** + * Initialize master (web workers). + * @private + */ -Parent.prototype.destroy = function destroy() { - global.close(); -}; + init() { + global.onerror = (event) => { + this.emit('error', new Error('Worker error.')); + }; + + global.onmessage = (event) => { + let data; + if (typeof event.data === 'string') { + data = Buffer.from(event.data, 'hex'); + assert(data.length === event.data.length / 2); + } else { + assert(event.data && typeof event.data === 'object'); + assert(event.data.data && typeof event.data.data.length === 'number'); + data = event.data.data; + data.__proto__ = Buffer.prototype; + } + this.emit('data', data); + }; + } + + /** + * Send data to parent process. + * @param {Buffer} data + * @returns {Boolean} + */ + + write(data) { + if (global.postMessage.length === 2) { + data.__proto__ = Uint8Array.prototype; + global.postMessage({ data }, [data]); + } else { + global.postMessage(data.toString('hex')); + } + return true; + } + + /** + * Destroy the parent process. + */ + + destroy() { + global.close(); + } +} /* * Expose diff --git a/lib/workers/parent.js b/lib/workers/parent.js index c0868680..b4a2da94 100644 --- a/lib/workers/parent.js +++ b/lib/workers/parent.js @@ -9,60 +9,63 @@ const EventEmitter = require('events'); /** + * Parent * Represents the parent process. * @alias module:workers.Parent - * @constructor + * @extends EventEmitter */ -function Parent() { - if (!(this instanceof Parent)) - return new Parent(); +class Parent extends EventEmitter { + /** + * Create the parent process. + * @constructor + */ - EventEmitter.call(this); + constructor() { + super(); - this.init(); + this.init(); + } + + /** + * Initialize master (node.js). + * @private + */ + + init() { + process.stdin.on('data', (data) => { + this.emit('data', data); + }); + + // Nowhere to send these errors: + process.stdin.on('error', () => {}); + process.stdout.on('error', () => {}); + process.stderr.on('error', () => {}); + + process.on('uncaughtException', (err) => { + this.emit('exception', err); + }); + } + + /** + * Send data to parent process. + * @param {Buffer} data + * @returns {Boolean} + */ + + write(data) { + return process.stdout.write(data); + } + + /** + * Destroy the parent process. + */ + + destroy() { + return process.exit(0); + } } -Object.setPrototypeOf(Parent.prototype, EventEmitter.prototype); - -/** - * Initialize master (node.js). - * @private - */ - -Parent.prototype.init = function init() { - process.stdin.on('data', (data) => { - this.emit('data', data); - }); - - // Nowhere to send these errors: - process.stdin.on('error', () => {}); - process.stdout.on('error', () => {}); - process.stderr.on('error', () => {}); - - process.on('uncaughtException', (err) => { - this.emit('exception', err); - }); -}; - -/** - * Send data to parent process. - * @param {Buffer} data - * @returns {Boolean} - */ - -Parent.prototype.write = function write(data) { - return process.stdout.write(data); -}; - -/** - * Destroy the parent process. - */ - -Parent.prototype.destroy = function destroy() { - return process.exit(0); -}; - /* * Expose */ diff --git a/lib/workers/parser.js b/lib/workers/parser.js index 26c21788..3550b60d 100644 --- a/lib/workers/parser.js +++ b/lib/workers/parser.js @@ -1,5 +1,5 @@ /*! - * workers.js - worker processes for bcoin + * parser.js - worker parser for bcoin * Copyright (c) 2014-2015, Fedor Indutny (MIT License) * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). * https://github.com/bcoin-org/bcoin @@ -14,178 +14,186 @@ const packets = require('./packets'); /** * Parser * @alias module:workers.Parser - * @constructor + * @extends EventEmitter */ -function Parser() { - if (!(this instanceof Parser)) - return new Parser(); +class Parser extends EventEmitter { + /** + * Create a parser. + * @constructor + */ - EventEmitter.call(this); + constructor() { + super(); - this.waiting = 9; - this.header = null; - this.pending = []; - this.total = 0; -} - -Object.setPrototypeOf(Parser.prototype, EventEmitter.prototype); - -Parser.prototype.feed = function feed(data) { - this.total += data.length; - this.pending.push(data); - - while (this.total >= this.waiting) { - const chunk = this.read(this.waiting); - this.parse(chunk); - } -}; - -Parser.prototype.read = function read(size) { - assert(this.total >= size, 'Reading too much.'); - - if (size === 0) - return Buffer.alloc(0); - - const pending = this.pending[0]; - - if (pending.length > size) { - const chunk = pending.slice(0, size); - this.pending[0] = pending.slice(size); - this.total -= chunk.length; - return chunk; + this.waiting = 9; + this.header = null; + this.pending = []; + this.total = 0; } - if (pending.length === size) { - const chunk = this.pending.shift(); - this.total -= chunk.length; - return chunk; + feed(data) { + this.total += data.length; + this.pending.push(data); + + while (this.total >= this.waiting) { + const chunk = this.read(this.waiting); + this.parse(chunk); + } } - const chunk = Buffer.allocUnsafe(size); - let off = 0; + read(size) { + assert(this.total >= size, 'Reading too much.'); + + if (size === 0) + return Buffer.alloc(0); - while (off < chunk.length) { const pending = this.pending[0]; - const len = pending.copy(chunk, off); - if (len === pending.length) - this.pending.shift(); - else - this.pending[0] = pending.slice(len); - off += len; + + if (pending.length > size) { + const chunk = pending.slice(0, size); + this.pending[0] = pending.slice(size); + this.total -= chunk.length; + return chunk; + } + + if (pending.length === size) { + const chunk = this.pending.shift(); + this.total -= chunk.length; + return chunk; + } + + const chunk = Buffer.allocUnsafe(size); + let off = 0; + + while (off < chunk.length) { + const pending = this.pending[0]; + const len = pending.copy(chunk, off); + if (len === pending.length) + this.pending.shift(); + else + this.pending[0] = pending.slice(len); + off += len; + } + + assert.strictEqual(off, chunk.length); + + this.total -= chunk.length; + + return chunk; } - assert.strictEqual(off, chunk.length); + parse(data) { + let header = this.header; - this.total -= chunk.length; + if (!header) { + try { + header = this.parseHeader(data); + } catch (e) { + this.emit('error', e); + return; + } - return chunk; -}; + this.header = header; + this.waiting = header.size + 1; -Parser.prototype.parse = function parse(data) { - let header = this.header; + return; + } - if (!header) { + this.waiting = 9; + this.header = null; + + let packet; try { - header = this.parseHeader(data); + packet = this.parsePacket(header, data); } catch (e) { this.emit('error', e); return; } - this.header = header; - this.waiting = header.size + 1; + if (data[data.length - 1] !== 0x0a) { + this.emit('error', new Error('No trailing newline.')); + return; + } - return; + packet.id = header.id; + + this.emit('packet', packet); } - this.waiting = 9; - this.header = null; - - let packet; - try { - packet = this.parsePacket(header, data); - } catch (e) { - this.emit('error', e); - return; + parseHeader(data) { + const id = data.readUInt32LE(0, true); + const cmd = data.readUInt8(4, true); + const size = data.readUInt32LE(5, true); + return new Header(id, cmd, size); } - if (data[data.length - 1] !== 0x0a) { - this.emit('error', new Error('No trailing newline.')); - return; + parsePacket(header, data) { + switch (header.cmd) { + case packets.types.ENV: + return packets.EnvPacket.fromRaw(data); + case packets.types.EVENT: + return packets.EventPacket.fromRaw(data); + case packets.types.LOG: + return packets.LogPacket.fromRaw(data); + case packets.types.ERROR: + return packets.ErrorPacket.fromRaw(data); + case packets.types.ERRORRESULT: + return packets.ErrorResultPacket.fromRaw(data); + case packets.types.CHECK: + return packets.CheckPacket.fromRaw(data); + case packets.types.CHECKRESULT: + return packets.CheckResultPacket.fromRaw(data); + case packets.types.SIGN: + return packets.SignPacket.fromRaw(data); + case packets.types.SIGNRESULT: + return packets.SignResultPacket.fromRaw(data); + case packets.types.CHECKINPUT: + return packets.CheckInputPacket.fromRaw(data); + case packets.types.CHECKINPUTRESULT: + return packets.CheckInputResultPacket.fromRaw(data); + case packets.types.SIGNINPUT: + return packets.SignInputPacket.fromRaw(data); + case packets.types.SIGNINPUTRESULT: + return packets.SignInputResultPacket.fromRaw(data); + case packets.types.ECVERIFY: + return packets.ECVerifyPacket.fromRaw(data); + case packets.types.ECVERIFYRESULT: + return packets.ECVerifyResultPacket.fromRaw(data); + case packets.types.ECSIGN: + return packets.ECSignPacket.fromRaw(data); + case packets.types.ECSIGNRESULT: + return packets.ECSignResultPacket.fromRaw(data); + case packets.types.MINE: + return packets.MinePacket.fromRaw(data); + case packets.types.MINERESULT: + return packets.MineResultPacket.fromRaw(data); + case packets.types.SCRYPT: + return packets.ScryptPacket.fromRaw(data); + case packets.types.SCRYPTRESULT: + return packets.ScryptResultPacket.fromRaw(data); + default: + throw new Error('Unknown packet.'); + } } - - packet.id = header.id; - - this.emit('packet', packet); -}; - -Parser.prototype.parseHeader = function parseHeader(data) { - const id = data.readUInt32LE(0, true); - const cmd = data.readUInt8(4, true); - const size = data.readUInt32LE(5, true); - return new Header(id, cmd, size); -}; - -Parser.prototype.parsePacket = function parsePacket(header, data) { - switch (header.cmd) { - case packets.types.ENV: - return packets.EnvPacket.fromRaw(data); - case packets.types.EVENT: - return packets.EventPacket.fromRaw(data); - case packets.types.LOG: - return packets.LogPacket.fromRaw(data); - case packets.types.ERROR: - return packets.ErrorPacket.fromRaw(data); - case packets.types.ERRORRESULT: - return packets.ErrorResultPacket.fromRaw(data); - case packets.types.CHECK: - return packets.CheckPacket.fromRaw(data); - case packets.types.CHECKRESULT: - return packets.CheckResultPacket.fromRaw(data); - case packets.types.SIGN: - return packets.SignPacket.fromRaw(data); - case packets.types.SIGNRESULT: - return packets.SignResultPacket.fromRaw(data); - case packets.types.CHECKINPUT: - return packets.CheckInputPacket.fromRaw(data); - case packets.types.CHECKINPUTRESULT: - return packets.CheckInputResultPacket.fromRaw(data); - case packets.types.SIGNINPUT: - return packets.SignInputPacket.fromRaw(data); - case packets.types.SIGNINPUTRESULT: - return packets.SignInputResultPacket.fromRaw(data); - case packets.types.ECVERIFY: - return packets.ECVerifyPacket.fromRaw(data); - case packets.types.ECVERIFYRESULT: - return packets.ECVerifyResultPacket.fromRaw(data); - case packets.types.ECSIGN: - return packets.ECSignPacket.fromRaw(data); - case packets.types.ECSIGNRESULT: - return packets.ECSignResultPacket.fromRaw(data); - case packets.types.MINE: - return packets.MinePacket.fromRaw(data); - case packets.types.MINERESULT: - return packets.MineResultPacket.fromRaw(data); - case packets.types.SCRYPT: - return packets.ScryptPacket.fromRaw(data); - case packets.types.SCRYPTRESULT: - return packets.ScryptResultPacket.fromRaw(data); - default: - throw new Error('Unknown packet.'); - } -}; +} /** * Header - * @constructor * @ignore */ -function Header(id, cmd, size) { - this.id = id; - this.cmd = cmd; - this.size = size; +class Header { + /** + * Create a header. + * @constructor + */ + + constructor(id, cmd, size) { + this.id = id; + this.cmd = cmd; + this.size = size; + } } /* diff --git a/lib/workers/workerpool.js b/lib/workers/workerpool.js index 9671d0ff..a9563444 100644 --- a/lib/workers/workerpool.js +++ b/lib/workers/workerpool.js @@ -20,654 +20,664 @@ const Framer = require('./framer'); const packets = require('./packets'); /** - * A worker pool. + * Worker Pool * @alias module:workers.WorkerPool - * @constructor - * @param {Object} options - * @param {Number} [options.size=num-cores] - Max pool size. - * @param {Number} [options.timeout=120000] - Execution timeout. + * @extends EventEmitter * @property {Number} size * @property {Number} timeout * @property {Map} children * @property {Number} uid */ -function WorkerPool(options) { - if (!(this instanceof WorkerPool)) - return new WorkerPool(options); +class WorkerPool extends EventEmitter { + /** + * Create a worker pool. + * @constructor + * @param {Object} options + * @param {Number} [options.size=num-cores] - Max pool size. + * @param {Number} [options.timeout=120000] - Execution timeout. + */ - EventEmitter.call(this); + constructor(options) { + super(); - this.enabled = false; - this.size = getCores(); - this.timeout = 120000; - this.file = process.env.BCOIN_WORKER_FILE || 'worker.js'; + this.enabled = false; + this.size = getCores(); + this.timeout = 120000; + this.file = process.env.BCOIN_WORKER_FILE || 'worker.js'; - this.children = new Map(); - this.uid = 0; + this.children = new Map(); + this.uid = 0; - this.set(options); + this.set(options); + } + + /** + * Set worker pool options. + * @param {Object} options + */ + + set(options) { + if (!options) + return; + + if (options.enabled != null) { + assert(typeof options.enabled === 'boolean'); + this.enabled = options.enabled; + } + + if (options.size != null) { + assert((options.size >>> 0) === options.size); + assert(options.size > 0); + this.size = options.size; + } + + if (options.timeout != null) { + assert(Number.isSafeInteger(options.timeout)); + assert(options.timeout >= -1); + this.timeout = options.timeout; + } + + if (options.file != null) { + assert(typeof options.file === 'string'); + this.file = options.file; + } + } + + /** + * Open worker pool. + * @returns {Promise} + */ + + async open() { + ; + } + + /** + * Close worker pool. + * @returns {Promise} + */ + + async close() { + this.destroy(); + } + + /** + * Spawn a new worker. + * @param {Number} id - Worker ID. + * @returns {Worker} + */ + + spawn(id) { + const child = new Worker(this.file); + + child.id = id; + + child.on('error', (err) => { + this.emit('error', err, child); + }); + + child.on('exit', (code) => { + this.emit('exit', code, child); + + if (this.children.get(id) === child) + this.children.delete(id); + }); + + child.on('event', (items) => { + this.emit('event', items, child); + this.emit(...items); + }); + + child.on('log', (text) => { + this.emit('log', text, child); + }); + + this.emit('spawn', child); + + return child; + } + + /** + * Allocate a new worker, will not go above `size` option + * and will automatically load balance the workers. + * @returns {Worker} + */ + + alloc() { + const id = this.uid++ % this.size; + + if (!this.children.has(id)) + this.children.set(id, this.spawn(id)); + + return this.children.get(id); + } + + /** + * Emit an event on the worker side (all workers). + * @param {String} event + * @param {...Object} arg + * @returns {Boolean} + */ + + sendEvent() { + let result = true; + + for (const child of this.children.values()) { + if (!child.sendEvent.apply(child, arguments)) + result = false; + } + + return result; + } + + /** + * Destroy all workers. + */ + + destroy() { + for (const child of this.children.values()) + child.destroy(); + } + + /** + * Call a method for a worker to execute. + * @param {Packet} packet + * @param {Number} timeout + * @returns {Promise} + */ + + execute(packet, timeout) { + if (!this.enabled || !Child.hasSupport()) { + return new Promise((resolve, reject) => { + setImmediate(() => { + let result; + try { + result = jobs.handle(packet); + } catch (e) { + reject(e); + return; + } + resolve(result); + }); + }); + } + + if (!timeout) + timeout = this.timeout; + + const child = this.alloc(); + + return child.execute(packet, timeout); + } + + /** + * Execute the tx check job (default timeout). + * @method + * @param {TX} tx + * @param {CoinView} view + * @param {VerifyFlags} flags + * @returns {Promise} + */ + + async check(tx, view, flags) { + const packet = new packets.CheckPacket(tx, view, flags); + const result = await this.execute(packet, -1); + + if (result.error) + throw result.error; + + return null; + } + + /** + * Execute the tx signing job (default timeout). + * @method + * @param {MTX} tx + * @param {KeyRing[]} ring + * @param {SighashType} type + * @returns {Promise} + */ + + async sign(tx, ring, type) { + let rings = ring; + + if (!Array.isArray(rings)) + rings = [rings]; + + const packet = new packets.SignPacket(tx, rings, type); + const result = await this.execute(packet, -1); + + result.inject(tx); + + return result.total; + } + + /** + * Execute the tx input check job (default timeout). + * @method + * @param {TX} tx + * @param {Number} index + * @param {Coin|Output} coin + * @param {VerifyFlags} flags + * @returns {Promise} + */ + + async checkInput(tx, index, coin, flags) { + const packet = new packets.CheckInputPacket(tx, index, coin, flags); + const result = await this.execute(packet, -1); + + if (result.error) + throw result.error; + + return null; + } + + /** + * Execute the tx input signing job (default timeout). + * @method + * @param {MTX} tx + * @param {Number} index + * @param {Coin|Output} coin + * @param {KeyRing} ring + * @param {SighashType} type + * @returns {Promise} + */ + + async signInput(tx, index, coin, ring, type) { + const packet = new packets.SignInputPacket(tx, index, coin, ring, type); + const result = await this.execute(packet, -1); + result.inject(tx); + return result.value; + } + + /** + * Execute the secp256k1 verify job (no timeout). + * @method + * @param {Buffer} msg + * @param {Buffer} sig - DER formatted. + * @param {Buffer} key + * @returns {Promise} + */ + + async ecVerify(msg, sig, key) { + const packet = new packets.ECVerifyPacket(msg, sig, key); + const result = await this.execute(packet, -1); + return result.value; + } + + /** + * Execute the secp256k1 signing job (no timeout). + * @method + * @param {Buffer} msg + * @param {Buffer} key + * @returns {Promise} + */ + + async ecSign(msg, key) { + const packet = new packets.ECSignPacket(msg, key); + const result = await this.execute(packet, -1); + return result.sig; + } + + /** + * Execute the mining job (no timeout). + * @method + * @param {Buffer} data + * @param {Buffer} target + * @param {Number} min + * @param {Number} max + * @returns {Promise} - Returns {Number}. + */ + + async mine(data, target, min, max) { + const packet = new packets.MinePacket(data, target, min, max); + const result = await this.execute(packet, -1); + return result.nonce; + } + + /** + * Execute scrypt job (no timeout). + * @method + * @param {Buffer} passwd + * @param {Buffer} salt + * @param {Number} N + * @param {Number} r + * @param {Number} p + * @param {Number} len + * @returns {Promise} + */ + + async scrypt(passwd, salt, N, r, p, len) { + const packet = new packets.ScryptPacket(passwd, salt, N, r, p, len); + const result = await this.execute(packet, -1); + return result.key; + } } -Object.setPrototypeOf(WorkerPool.prototype, EventEmitter.prototype); - /** - * Set worker pool options. - * @param {Object} options + * Worker + * @alias module:workers.Worker + * @extends EventEmitter */ -WorkerPool.prototype.set = function set(options) { - if (!options) - return; +class Worker extends EventEmitter { + /** + * Create a worker. + * @constructor + * @param {String} file + */ - if (options.enabled != null) { - assert(typeof options.enabled === 'boolean'); - this.enabled = options.enabled; + constructor(file) { + super(); + + this.id = -1; + this.framer = new Framer(); + this.parser = new Parser(); + this.pending = new Map(); + + this.child = new Child(file); + + this.init(); } - if (options.size != null) { - assert((options.size >>> 0) === options.size); - assert(options.size > 0); - this.size = options.size; + /** + * Initialize worker. Bind to events. + * @private + */ + + init() { + this.child.on('data', (data) => { + this.parser.feed(data); + }); + + this.child.on('exit', (code, signal) => { + this.emit('exit', code, signal); + }); + + this.child.on('error', (err) => { + this.emit('error', err); + }); + + this.parser.on('error', (err) => { + this.emit('error', err); + }); + + this.parser.on('packet', (packet) => { + this.emit('packet', packet); + }); + + this.listen(); } - if (options.timeout != null) { - assert(Number.isSafeInteger(options.timeout)); - assert(options.timeout >= -1); - this.timeout = options.timeout; - } + /** + * Listen for packets. + * @private + */ - if (options.file != null) { - assert(typeof options.file === 'string'); - this.file = options.file; - } -}; + listen() { + this.on('exit', (code, signal) => { + this.killJobs(); + }); -/** - * Open worker pool. - * @returns {Promise} - */ + this.on('error', (err) => { + this.killJobs(); + }); -WorkerPool.prototype.open = async function open() { - ; -}; + this.on('packet', (packet) => { + try { + this.handlePacket(packet); + } catch (e) { + this.emit('error', e); + } + }); -/** - * Close worker pool. - * @returns {Promise} - */ - -WorkerPool.prototype.close = async function close() { - this.destroy(); -}; - -/** - * Spawn a new worker. - * @param {Number} id - Worker ID. - * @returns {Worker} - */ - -WorkerPool.prototype.spawn = function spawn(id) { - const child = new Worker(this.file); - - child.id = id; - - child.on('error', (err) => { - this.emit('error', err, child); - }); - - child.on('exit', (code) => { - this.emit('exit', code, child); - - if (this.children.get(id) === child) - this.children.delete(id); - }); - - child.on('event', (items) => { - this.emit('event', items, child); - this.emit(...items); - }); - - child.on('log', (text) => { - this.emit('log', text, child); - }); - - this.emit('spawn', child); - - return child; -}; - -/** - * Allocate a new worker, will not go above `size` option - * and will automatically load balance the workers. - * @returns {Worker} - */ - -WorkerPool.prototype.alloc = function alloc() { - const id = this.uid++ % this.size; - - if (!this.children.has(id)) - this.children.set(id, this.spawn(id)); - - return this.children.get(id); -}; - -/** - * Emit an event on the worker side (all workers). - * @param {String} event - * @param {...Object} arg - * @returns {Boolean} - */ - -WorkerPool.prototype.sendEvent = function sendEvent() { - let result = true; - - for (const child of this.children.values()) { - if (!child.sendEvent.apply(child, arguments)) - result = false; - } - - return result; -}; - -/** - * Destroy all workers. - */ - -WorkerPool.prototype.destroy = function destroy() { - for (const child of this.children.values()) - child.destroy(); -}; - -/** - * Call a method for a worker to execute. - * @param {Packet} packet - * @param {Number} timeout - * @returns {Promise} - */ - -WorkerPool.prototype.execute = function execute(packet, timeout) { - if (!this.enabled || !Child.hasSupport()) { - return new Promise((resolve, reject) => { - setImmediate(() => { - let result; - try { - result = jobs.handle(packet); - } catch (e) { - reject(e); - return; - } - resolve(result); - }); + this.sendEnv({ + BCOIN_WORKER_NETWORK: Network.type, + BCOIN_WORKER_ISTTY: process.stdout + ? (process.stdout.isTTY ? '1' : '0') + : '0' }); } - if (!timeout) - timeout = this.timeout; + /** + * Handle packet. + * @private + * @param {Packet} packet + */ - const child = this.alloc(); - - return child.execute(packet, timeout); -}; - -/** - * Execute the tx check job (default timeout). - * @method - * @param {TX} tx - * @param {CoinView} view - * @param {VerifyFlags} flags - * @returns {Promise} - */ - -WorkerPool.prototype.check = async function check(tx, view, flags) { - const packet = new packets.CheckPacket(tx, view, flags); - const result = await this.execute(packet, -1); - - if (result.error) - throw result.error; - - return null; -}; - -/** - * Execute the tx signing job (default timeout). - * @method - * @param {MTX} tx - * @param {KeyRing[]} ring - * @param {SighashType} type - * @returns {Promise} - */ - -WorkerPool.prototype.sign = async function sign(tx, ring, type) { - let rings = ring; - - if (!Array.isArray(rings)) - rings = [rings]; - - const packet = new packets.SignPacket(tx, rings, type); - const result = await this.execute(packet, -1); - - result.inject(tx); - - return result.total; -}; - -/** - * Execute the tx input check job (default timeout). - * @method - * @param {TX} tx - * @param {Number} index - * @param {Coin|Output} coin - * @param {VerifyFlags} flags - * @returns {Promise} - */ - -WorkerPool.prototype.checkInput = async function checkInput(tx, index, coin, flags) { - const packet = new packets.CheckInputPacket(tx, index, coin, flags); - const result = await this.execute(packet, -1); - - if (result.error) - throw result.error; - - return null; -}; - -/** - * Execute the tx input signing job (default timeout). - * @method - * @param {MTX} tx - * @param {Number} index - * @param {Coin|Output} coin - * @param {KeyRing} ring - * @param {SighashType} type - * @returns {Promise} - */ - -WorkerPool.prototype.signInput = async function signInput(tx, index, coin, ring, type) { - const packet = new packets.SignInputPacket(tx, index, coin, ring, type); - const result = await this.execute(packet, -1); - result.inject(tx); - return result.value; -}; - -/** - * Execute the secp256k1 verify job (no timeout). - * @method - * @param {Buffer} msg - * @param {Buffer} sig - DER formatted. - * @param {Buffer} key - * @returns {Promise} - */ - -WorkerPool.prototype.ecVerify = async function ecVerify(msg, sig, key) { - const packet = new packets.ECVerifyPacket(msg, sig, key); - const result = await this.execute(packet, -1); - return result.value; -}; - -/** - * Execute the secp256k1 signing job (no timeout). - * @method - * @param {Buffer} msg - * @param {Buffer} key - * @returns {Promise} - */ - -WorkerPool.prototype.ecSign = async function ecSign(msg, key) { - const packet = new packets.ECSignPacket(msg, key); - const result = await this.execute(packet, -1); - return result.sig; -}; - -/** - * Execute the mining job (no timeout). - * @method - * @param {Buffer} data - * @param {Buffer} target - * @param {Number} min - * @param {Number} max - * @returns {Promise} - Returns {Number}. - */ - -WorkerPool.prototype.mine = async function mine(data, target, min, max) { - const packet = new packets.MinePacket(data, target, min, max); - const result = await this.execute(packet, -1); - return result.nonce; -}; - -/** - * Execute scrypt job (no timeout). - * @method - * @param {Buffer} passwd - * @param {Buffer} salt - * @param {Number} N - * @param {Number} r - * @param {Number} p - * @param {Number} len - * @returns {Promise} - */ - -WorkerPool.prototype.scrypt = async function scrypt(passwd, salt, N, r, p, len) { - const packet = new packets.ScryptPacket(passwd, salt, N, r, p, len); - const result = await this.execute(packet, -1); - return result.key; -}; - -/** - * Represents a worker. - * @alias module:workers.Worker - * @constructor - * @param {String} file - */ - -function Worker(file) { - if (!(this instanceof Worker)) - return new Worker(file); - - EventEmitter.call(this); - - this.id = -1; - this.framer = new Framer(); - this.parser = new Parser(); - this.pending = new Map(); - - this.child = new Child(file); - - this.init(); -} - -Object.setPrototypeOf(Worker.prototype, EventEmitter.prototype); - -/** - * Initialize worker. Bind to events. - * @private - */ - -Worker.prototype.init = function init() { - this.child.on('data', (data) => { - this.parser.feed(data); - }); - - this.child.on('exit', (code, signal) => { - this.emit('exit', code, signal); - }); - - this.child.on('error', (err) => { - this.emit('error', err); - }); - - this.parser.on('error', (err) => { - this.emit('error', err); - }); - - this.parser.on('packet', (packet) => { - this.emit('packet', packet); - }); - - this.listen(); -}; - -/** - * Listen for packets. - * @private - */ - -Worker.prototype.listen = function listen() { - this.on('exit', (code, signal) => { - this.killJobs(); - }); - - this.on('error', (err) => { - this.killJobs(); - }); - - this.on('packet', (packet) => { - try { - this.handlePacket(packet); - } catch (e) { - this.emit('error', e); + handlePacket(packet) { + switch (packet.cmd) { + case packets.types.EVENT: + this.emit('event', packet.items); + this.emit(...packet.items); + break; + case packets.types.LOG: + this.emit('log', packet.text); + break; + case packets.types.ERROR: + this.emit('error', packet.error); + break; + case packets.types.ERRORRESULT: + this.rejectJob(packet.id, packet.error); + break; + default: + this.resolveJob(packet.id, packet); + break; } - }); - - this.sendEnv({ - BCOIN_WORKER_NETWORK: Network.type, - BCOIN_WORKER_ISTTY: process.stdout - ? (process.stdout.isTTY ? '1' : '0') - : '0' - }); -}; - -/** - * Handle packet. - * @private - * @param {Packet} packet - */ - -Worker.prototype.handlePacket = function handlePacket(packet) { - switch (packet.cmd) { - case packets.types.EVENT: - this.emit('event', packet.items); - this.emit(...packet.items); - break; - case packets.types.LOG: - this.emit('log', packet.text); - break; - case packets.types.ERROR: - this.emit('error', packet.error); - break; - case packets.types.ERRORRESULT: - this.rejectJob(packet.id, packet.error); - break; - default: - this.resolveJob(packet.id, packet); - break; } -}; -/** - * Send data to worker. - * @param {Buffer} data - * @returns {Boolean} - */ + /** + * Send data to worker. + * @param {Buffer} data + * @returns {Boolean} + */ -Worker.prototype.write = function write(data) { - return this.child.write(data); -}; + write(data) { + return this.child.write(data); + } -/** - * Frame and send a packet. - * @param {Packet} packet - * @returns {Boolean} - */ + /** + * Frame and send a packet. + * @param {Packet} packet + * @returns {Boolean} + */ -Worker.prototype.send = function send(packet) { - return this.write(this.framer.packet(packet)); -}; + send(packet) { + return this.write(this.framer.packet(packet)); + } -/** - * Send environment. - * @param {Object} env - * @returns {Boolean} - */ + /** + * Send environment. + * @param {Object} env + * @returns {Boolean} + */ -Worker.prototype.sendEnv = function sendEnv(env) { - return this.send(new packets.EnvPacket(env)); -}; + sendEnv(env) { + return this.send(new packets.EnvPacket(env)); + } -/** - * Emit an event on the worker side. - * @param {String} event - * @param {...Object} arg - * @returns {Boolean} - */ + /** + * Emit an event on the worker side. + * @param {String} event + * @param {...Object} arg + * @returns {Boolean} + */ -Worker.prototype.sendEvent = function sendEvent(...items) { - return this.send(new packets.EventPacket(items)); -}; + sendEvent(...items) { + return this.send(new packets.EventPacket(items)); + } -/** - * Destroy the worker. - */ + /** + * Destroy the worker. + */ -Worker.prototype.destroy = function destroy() { - return this.child.destroy(); -}; + destroy() { + return this.child.destroy(); + } -/** - * Call a method for a worker to execute. - * @param {Packet} packet - * @param {Number} timeout - * @returns {Promise} - */ + /** + * Call a method for a worker to execute. + * @param {Packet} packet + * @param {Number} timeout + * @returns {Promise} + */ -Worker.prototype.execute = function execute(packet, timeout) { - return new Promise((resolve, reject) => { - this._execute(packet, timeout, resolve, reject); - }); -}; + execute(packet, timeout) { + return new Promise((resolve, reject) => { + this._execute(packet, timeout, resolve, reject); + }); + } -/** - * Call a method for a worker to execute. - * @private - * @param {Packet} packet - * @param {Number} timeout - * @param {Function} resolve - * @param {Function} reject - * the worker method specifies. - */ + /** + * Call a method for a worker to execute. + * @private + * @param {Packet} packet + * @param {Number} timeout + * @param {Function} resolve + * @param {Function} reject + * the worker method specifies. + */ -Worker.prototype._execute = function _execute(packet, timeout, resolve, reject) { - const job = new PendingJob(this, packet.id, resolve, reject); + _execute(packet, timeout, resolve, reject) { + const job = new PendingJob(this, packet.id, resolve, reject); - assert(!this.pending.has(packet.id), 'ID overflow.'); + assert(!this.pending.has(packet.id), 'ID overflow.'); - this.pending.set(packet.id, job); + this.pending.set(packet.id, job); - job.start(timeout); + job.start(timeout); - this.send(packet); -}; + this.send(packet); + } -/** - * Resolve a job. - * @param {Number} id - * @param {Packet} result - */ + /** + * Resolve a job. + * @param {Number} id + * @param {Packet} result + */ -Worker.prototype.resolveJob = function resolveJob(id, result) { - const job = this.pending.get(id); + resolveJob(id, result) { + const job = this.pending.get(id); - if (!job) - throw new Error(`Job ${id} is not in progress.`); + if (!job) + throw new Error(`Job ${id} is not in progress.`); - job.resolve(result); -}; + job.resolve(result); + } -/** - * Reject a job. - * @param {Number} id - * @param {Error} err - */ + /** + * Reject a job. + * @param {Number} id + * @param {Error} err + */ -Worker.prototype.rejectJob = function rejectJob(id, err) { - const job = this.pending.get(id); + rejectJob(id, err) { + const job = this.pending.get(id); - if (!job) - throw new Error(`Job ${id} is not in progress.`); + if (!job) + throw new Error(`Job ${id} is not in progress.`); - job.reject(err); -}; + job.reject(err); + } -/** - * Kill all jobs associated with worker. - */ + /** + * Kill all jobs associated with worker. + */ -Worker.prototype.killJobs = function killJobs() { - for (const job of this.pending.values()) - job.destroy(); -}; + killJobs() { + for (const job of this.pending.values()) + job.destroy(); + } +} /** * Pending Job - * @constructor * @ignore - * @param {Worker} worker - * @param {Number} id - * @param {Function} resolve - * @param {Function} reject */ -function PendingJob(worker, id, resolve, reject) { - this.worker = worker; - this.id = id; - this.job = { resolve, reject }; - this.timer = null; -} +class PendingJob { + /** + * Create a pending job. + * @constructor + * @param {Worker} worker + * @param {Number} id + * @param {Function} resolve + * @param {Function} reject + */ -/** - * Start the timer. - * @param {Number} timeout - */ - -PendingJob.prototype.start = function start(timeout) { - if (!timeout || timeout <= 0) - return; - - this.timer = setTimeout(() => { - this.reject(new Error('Worker timed out.')); - }, timeout); -}; - -/** - * Destroy the job with an error. - */ - -PendingJob.prototype.destroy = function destroy() { - this.reject(new Error('Job was destroyed.')); -}; - -/** - * Cleanup job state. - * @returns {Job} - */ - -PendingJob.prototype.cleanup = function cleanup() { - const job = this.job; - - assert(job, 'Already finished.'); - - this.job = null; - - if (this.timer != null) { - clearTimeout(this.timer); + constructor(worker, id, resolve, reject) { + this.worker = worker; + this.id = id; + this.job = { resolve, reject }; this.timer = null; } - assert(this.worker.pending.has(this.id)); - this.worker.pending.delete(this.id); + /** + * Start the timer. + * @param {Number} timeout + */ - return job; -}; + start(timeout) { + if (!timeout || timeout <= 0) + return; -/** - * Complete job with result. - * @param {Object} result - */ + this.timer = setTimeout(() => { + this.reject(new Error('Worker timed out.')); + }, timeout); + } -PendingJob.prototype.resolve = function resolve(result) { - const job = this.cleanup(); - job.resolve(result); -}; + /** + * Destroy the job with an error. + */ -/** - * Complete job with error. - * @param {Error} err - */ + destroy() { + this.reject(new Error('Job was destroyed.')); + } -PendingJob.prototype.reject = function reject(err) { - const job = this.cleanup(); - job.reject(err); -}; + /** + * Cleanup job state. + * @returns {Job} + */ + + cleanup() { + const job = this.job; + + assert(job, 'Already finished.'); + + this.job = null; + + if (this.timer != null) { + clearTimeout(this.timer); + this.timer = null; + } + + assert(this.worker.pending.has(this.id)); + this.worker.pending.delete(this.id); + + return job; + } + + /** + * Complete job with result. + * @param {Object} result + */ + + resolve(result) { + const job = this.cleanup(); + job.resolve(result); + } + + /** + * Complete job with error. + * @param {Error} err + */ + + reject(err) { + const job = this.cleanup(); + job.reject(err); + } +} /* * Helpers