diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index adeac841..2351f246 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -253,6 +253,18 @@ MTX.prototype.check = function check(flags) { return TX.prototype.check.call(this, this.view, flags); }; +/** + * Verify the transaction inputs on the worker pool + * (if workers are enabled). + * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] + * @param {WorkerPool?} pool + * @returns {Promise} + */ + +MTX.prototype.checkAsync = function checkAsync(flags, pool) { + return TX.prototype.checkAsync.call(this, this.view, flags, pool); +}; + /** * Verify all transaction inputs. * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] @@ -278,8 +290,15 @@ MTX.prototype.verify = function verify(flags) { * @returns {Promise} */ -MTX.prototype.verifyAsync = function verifyAsync(flags, pool) { - return TX.prototype.verifyAsync.call(this, this.view, flags, pool); +MTX.prototype.verifyAsync = async function verifyAsync(flags, pool) { + try { + await this.checkAsync(flags, pool); + } catch (e) { + if (e.type === 'ScriptError') + return false; + throw e; + } + return true; }; /** diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 8924ba25..ae9de01b 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -806,6 +806,50 @@ TX.prototype.checkInput = function checkInput(index, coin, flags) { ); }; +/** + * Verify the transaction inputs on the worker pool + * (if workers are enabled). + * @param {CoinView} view + * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] + * @param {WorkerPool?} pool + * @returns {Promise} + */ + +TX.prototype.checkAsync = async function checkAsync(view, flags, pool) { + if (this.inputs.length === 0) + throw new ScriptError('UNKNOWN_ERROR', 'No inputs.'); + + if (this.isCoinbase()) + return; + + if (!pool) + return this.check(view, flags); + + return await pool.check(this, view, flags); +}; + +/** + * Verify a transaction input asynchronously. + * @param {Number} index - Index of output being + * verified. + * @param {Coin|Output} coin - Previous output. + * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] + * @param {WorkerPool?} pool + * @returns {Promise} + */ + +TX.prototype.checkInputAsync = async function checkInputAsync(index, coin, flags, pool) { + let input = this.inputs[index]; + + assert(input, 'Input does not exist.'); + assert(coin, 'No coin passed.'); + + if (!pool) + return this.checkInput(index, coin, flags); + + return await pool.checkInput(this, index, coin, flags); +}; + /** * Verify all transaction inputs. * @param {CoinView} view @@ -854,16 +898,14 @@ TX.prototype.verifyInput = function verifyInput(index, coin, flags) { */ TX.prototype.verifyAsync = async function verifyAsync(view, flags, pool) { - if (this.inputs.length === 0) - return false; - - if (this.isCoinbase()) - return true; - - if (!pool) - return this.verify(view, flags); - - return await pool.verify(this, view, flags); + try { + await this.checkAsync(view, flags, pool); + } catch (e) { + if (e.type === 'ScriptError') + return false; + throw e; + } + return true; }; /** @@ -877,14 +919,14 @@ TX.prototype.verifyAsync = async function verifyAsync(view, flags, pool) { */ TX.prototype.verifyInputAsync = async function verifyInputAsync(index, coin, flags, pool) { - let input = this.inputs[index]; - - assert(input, 'Input does not exist.'); - - if (!pool) - return this.verifyInput(index, coin, flags); - - return await pool.verifyInput(this, index, coin, flags); + try { + await this.checkInput(index, coin, flags, pool); + } catch (e) { + if (e.type === 'ScriptError') + return false; + throw e; + } + return true; }; /** diff --git a/lib/workers/jobs.js b/lib/workers/jobs.js index 1a6bf222..b20b523e 100644 --- a/lib/workers/jobs.js +++ b/lib/workers/jobs.js @@ -43,10 +43,10 @@ jobs.execute = function execute(p) { jobs.handle = function handle(p) { switch (p.cmd) { - case packets.types.VERIFY: - return jobs.verify(p.tx, p.view, p.flags); - case packets.types.VERIFYINPUT: - return jobs.verifyInput(p.tx, p.index, p.coin, p.flags); + case packets.types.CHECK: + return jobs.check(p.tx, p.view, p.flags); + case packets.types.CHECKINPUT: + return jobs.checkInput(p.tx, p.index, p.coin, p.flags); case packets.types.SIGN: return jobs.sign(p.tx, p.rings, p.type); case packets.types.SIGNINPUT: @@ -65,22 +65,31 @@ jobs.handle = function handle(p) { }; /** - * Execute tx.verify() on worker. - * @see TX#verify + * Execute tx.check() on worker. + * @see TX#check * @param {TX} tx * @param {CoinView} view * @param {VerifyFlags} flags * @returns {Boolean} */ -jobs.verify = function verify(tx, view, flags) { - let result = tx.verify(view, flags); - return new packets.VerifyResultPacket(result); +jobs.check = function check(tx, view, flags) { + let err = null; + + try { + tx.check(view, flags); + } catch (e) { + if (e.type !== 'ScriptError') + throw e; + err = e; + } + + return new packets.CheckResultPacket(err); }; /** - * Execute tx.verifyInput() on worker. - * @see TX#verifyInput + * Execute tx.checkInput() on worker. + * @see TX#checkInput * @param {TX} tx * @param {Number} index * @param {Output} coin @@ -88,9 +97,18 @@ jobs.verify = function verify(tx, view, flags) { * @returns {Boolean} */ -jobs.verifyInput = function verifyInput(tx, index, coin, flags) { - let result = tx.verifyInput(index, coin, flags); - return new packets.VerifyInputResultPacket(result); +jobs.checkInput = function checkInput(tx, index, coin, flags) { + let err = null; + + try { + tx.checkInput(index, coin, flags); + } catch (e) { + if (e.type !== 'ScriptError') + throw e; + err = e; + } + + return new packets.CheckInputResultPacket(err); }; /** diff --git a/lib/workers/packets.js b/lib/workers/packets.js index bbc65455..6721667c 100644 --- a/lib/workers/packets.js +++ b/lib/workers/packets.js @@ -21,6 +21,7 @@ const MTX = require('../primitives/mtx'); const TX = require('../primitives/tx'); const KeyRing = require('../primitives/keyring'); const CoinView = require('../coins/coinview'); +const {ScriptError} = require('../script/common'); /* * Constants @@ -32,12 +33,12 @@ const packetTypes = { LOG: 2, ERROR: 3, ERRORRESULT: 4, - VERIFY: 5, - VERIFYRESULT: 6, + CHECK: 5, + CHECKRESULT: 6, SIGN: 7, SIGNRESULT: 8, - VERIFYINPUT: 9, - VERIFYINPUTRESULT: 10, + CHECKINPUT: 9, + CHECKINPUTRESULT: 10, SIGNINPUT: 11, SIGNINPUTRESULT: 12, ECVERIFY: 13, @@ -253,38 +254,37 @@ util.inherits(ErrorResultPacket, ErrorPacket); ErrorResultPacket.prototype.cmd = packetTypes.ERRORRESULT; /** - * VerifyPacket + * CheckPacket * @constructor */ -function VerifyPacket(tx, view, flags) { +function CheckPacket(tx, view, flags) { Packet.call(this); this.tx = tx || null; this.view = view || null; this.flags = flags != null ? flags : null; } -util.inherits(VerifyPacket, Packet); +util.inherits(CheckPacket, Packet); -VerifyPacket.prototype.cmd = packetTypes.VERIFY; +CheckPacket.prototype.cmd = packetTypes.CHECK; -VerifyPacket.prototype.getSize = function getSize() { +CheckPacket.prototype.getSize = function getSize() { return this.tx.getSize() + this.view.getSize(this.tx) + 4; }; -VerifyPacket.prototype.toWriter = function toWriter(bw) { +CheckPacket.prototype.toWriter = function toWriter(bw) { this.tx.toWriter(bw); this.view.toWriter(bw, this.tx); bw.write32(this.flags != null ? this.flags : -1); }; -VerifyPacket.fromRaw = function fromRaw(data) { +CheckPacket.fromRaw = function fromRaw(data) { let br = new BufferReader(data, true); - let packet = new VerifyPacket(); + let packet = new CheckPacket(); packet.tx = TX.fromReader(br); packet.view = CoinView.fromReader(br, packet.tx); - packet.flags = br.read32(); if (packet.flags === -1) @@ -294,31 +294,77 @@ VerifyPacket.fromRaw = function fromRaw(data) { }; /** - * VerifyResultPacket + * CheckResultPacket * @constructor */ -function VerifyResultPacket(value) { +function CheckResultPacket(error) { Packet.call(this); - this.value = value; + this.error = error || null; } -util.inherits(VerifyResultPacket, Packet); +util.inherits(CheckResultPacket, Packet); -VerifyResultPacket.prototype.cmd = packetTypes.VERIFYRESULT; +CheckResultPacket.prototype.cmd = packetTypes.CHECKRESULT; -VerifyResultPacket.prototype.getSize = function getSize() { - return 1; +CheckResultPacket.prototype.getSize = function getSize() { + let err = this.error; + let size = 0; + + if (!err) { + size += 1; + return size; + } + + size += 1; + size += encoding.sizeVarString(err.code, 'utf8'); + size += encoding.sizeVarString(err.message, 'utf8'); + size += encoding.sizeVarString(err.stack + '', 'utf8'); + size += 1; + size += 4; + + return size; }; -VerifyResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeU8(this.value ? 1 : 0); +CheckResultPacket.prototype.toWriter = function toWriter(bw) { + let err = this.error; + + if (!err) { + bw.writeU8(0); + return; + } + + bw.writeU8(1); + bw.writeVarString(err.code, 'utf8'); + bw.writeVarString(err.message, 'utf8'); + bw.writeVarString(err.stack + '', 'utf8'); + bw.writeU8(err.op === -1 ? 0xff : err.op); + bw.writeU32(err.ip === -1 ? 0xffffffff : err.ip); }; -VerifyResultPacket.fromRaw = function fromRaw(data) { +CheckResultPacket.fromRaw = function fromRaw(data) { let br = new BufferReader(data, true); - let packet = new VerifyResultPacket(); - packet.value = br.readU8() === 1; + let packet = new CheckResultPacket(); + let err; + + if (br.readU8() === 0) + return packet; + + err = new ScriptError(''); + err.code = br.readVarString('utf8'); + err.message = br.readVarString('utf8'); + err.stack = 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; + + packet.error = err; + return packet; }; @@ -340,13 +386,12 @@ SignPacket.prototype.cmd = packetTypes.SIGN; SignPacket.prototype.getSize = function getSize() { let size = 0; - let ring; size += this.tx.getSize(); size += this.tx.view.getSize(this.tx); size += encoding.sizeVarint(this.rings.length); - for (ring of this.rings) + for (let ring of this.rings) size += ring.getSize(); size += 1; @@ -355,14 +400,12 @@ SignPacket.prototype.getSize = function getSize() { }; SignPacket.prototype.toWriter = function toWriter(bw) { - let ring; - this.tx.toWriter(bw); this.tx.view.toWriter(bw, this.tx); bw.writeVarint(this.rings.length); - for (ring of this.rings) + for (let ring of this.rings) ring.toWriter(bw); bw.writeU8(this.type); @@ -472,11 +515,11 @@ SignResultPacket.fromRaw = function fromRaw(data) { }; /** - * VerifyInputPacket + * CheckInputPacket * @constructor */ -function VerifyInputPacket(tx, index, coin, flags) { +function CheckInputPacket(tx, index, coin, flags) { Packet.call(this); this.tx = tx || null; this.index = index; @@ -484,11 +527,11 @@ function VerifyInputPacket(tx, index, coin, flags) { this.flags = flags != null ? flags : null; } -util.inherits(VerifyInputPacket, Packet); +util.inherits(CheckInputPacket, Packet); -VerifyInputPacket.prototype.cmd = packetTypes.VERIFYINPUT; +CheckInputPacket.prototype.cmd = packetTypes.CHECKINPUT; -VerifyInputPacket.prototype.getSize = function getSize() { +CheckInputPacket.prototype.getSize = function getSize() { let size = 0; size += this.tx.getSize(); size += encoding.sizeVarint(this.index); @@ -498,7 +541,7 @@ VerifyInputPacket.prototype.getSize = function getSize() { return size; }; -VerifyInputPacket.prototype.toWriter = function toWriter(bw) { +CheckInputPacket.prototype.toWriter = function toWriter(bw) { this.tx.toWriter(bw); bw.writeVarint(this.index); bw.writeVarint(this.coin.value); @@ -506,9 +549,9 @@ VerifyInputPacket.prototype.toWriter = function toWriter(bw) { bw.write32(this.flags != null ? this.flags : -1); }; -VerifyInputPacket.fromRaw = function fromRaw(data) { +CheckInputPacket.fromRaw = function fromRaw(data) { let br = new BufferReader(data, true); - let packet = new VerifyInputPacket(); + let packet = new CheckInputPacket(); packet.tx = TX.fromReader(br); packet.index = br.readVarint(); @@ -526,31 +569,22 @@ VerifyInputPacket.fromRaw = function fromRaw(data) { }; /** - * VerifyInputResultPacket + * CheckInputResultPacket * @constructor */ -function VerifyInputResultPacket(value) { - Packet.call(this); - this.value = value; +function CheckInputResultPacket(error) { + CheckResultPacket.call(this, error); } -util.inherits(VerifyInputResultPacket, Packet); +util.inherits(CheckInputResultPacket, CheckResultPacket); -VerifyInputResultPacket.prototype.cmd = packetTypes.VERIFYINPUTRESULT; +CheckInputResultPacket.prototype.cmd = packetTypes.CHECKINPUTRESULT; -VerifyInputResultPacket.prototype.getSize = function getSize() { - return 1; -}; - -VerifyInputResultPacket.prototype.toWriter = function toWriter(bw) { - bw.writeU8(this.value ? 1 : 0); -}; - -VerifyInputResultPacket.fromRaw = function fromRaw(data) { - let br = new BufferReader(data, true); - let packet = new VerifyInputResultPacket(); - packet.value = br.readU8() === 1; +CheckInputResultPacket.fromRaw = function fromRaw(data) { + let p = CheckResultPacket.fromRaw(data); + let packet = new CheckInputResultPacket(); + packet.error = p.error; return packet; }; @@ -952,12 +986,12 @@ exports.EventPacket = EventPacket; exports.LogPacket = LogPacket; exports.ErrorPacket = ErrorPacket; exports.ErrorResultPacket = ErrorResultPacket; -exports.VerifyPacket = VerifyPacket; -exports.VerifyResultPacket = VerifyResultPacket; +exports.CheckPacket = CheckPacket; +exports.CheckResultPacket = CheckResultPacket; exports.SignPacket = SignPacket; exports.SignResultPacket = SignResultPacket; -exports.VerifyInputPacket = VerifyInputPacket; -exports.VerifyInputResultPacket = VerifyInputResultPacket; +exports.CheckInputPacket = CheckInputPacket; +exports.CheckInputResultPacket = CheckInputResultPacket; exports.SignInputPacket = SignInputPacket; exports.SignInputResultPacket = SignInputResultPacket; exports.ECVerifyPacket = ECVerifyPacket; diff --git a/lib/workers/parser.js b/lib/workers/parser.js index d3c74b09..13cbb012 100644 --- a/lib/workers/parser.js +++ b/lib/workers/parser.js @@ -143,18 +143,18 @@ Parser.prototype.parsePacket = function parsePacket(header, data) { return packets.ErrorPacket.fromRaw(data); case packets.types.ERRORRESULT: return packets.ErrorResultPacket.fromRaw(data); - case packets.types.VERIFY: - return packets.VerifyPacket.fromRaw(data); - case packets.types.VERIFYRESULT: - return packets.VerifyResultPacket.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.VERIFYINPUT: - return packets.VerifyInputPacket.fromRaw(data); - case packets.types.VERIFYINPUTRESULT: - return packets.VerifyInputResultPacket.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: diff --git a/lib/workers/workerpool.js b/lib/workers/workerpool.js index 942d67fc..d888ee08 100644 --- a/lib/workers/workerpool.js +++ b/lib/workers/workerpool.js @@ -211,18 +211,22 @@ WorkerPool.prototype.execute = function execute(packet, timeout) { }; /** - * Execute the tx verification job (default timeout). + * Execute the tx check job (default timeout). * @method * @param {TX} tx * @param {CoinView} view * @param {VerifyFlags} flags - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ -WorkerPool.prototype.verify = async function verify(tx, view, flags) { - let packet = new packets.VerifyPacket(tx, view, flags); +WorkerPool.prototype.check = async function check(tx, view, flags) { + let packet = new packets.CheckPacket(tx, view, flags); let result = await this.execute(packet, -1); - return result.value; + + if (result.error) + throw result.error; + + return null; }; /** @@ -250,19 +254,23 @@ WorkerPool.prototype.sign = async function sign(tx, ring, type) { }; /** - * Execute the tx input verification job (default timeout). + * Execute the tx input check job (default timeout). * @method * @param {TX} tx * @param {Number} index * @param {Coin|Output} coin * @param {VerifyFlags} flags - * @returns {Promise} - Returns Boolean. + * @returns {Promise} */ -WorkerPool.prototype.verifyInput = async function verifyInput(tx, index, coin, flags) { - let packet = new packets.VerifyInputPacket(tx, index, coin, flags); +WorkerPool.prototype.checkInput = async function checkInput(tx, index, coin, flags) { + let packet = new packets.CheckInputPacket(tx, index, coin, flags); let result = await this.execute(packet, -1); - return result.value; + + if (result.error) + throw result.error; + + return null; }; /**