workers: better errors for verification.

This commit is contained in:
Christopher Jeffrey 2017-07-13 17:11:48 -07:00
parent 053561f2ba
commit 498264b417
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 233 additions and 112 deletions

View File

@ -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;
};
/**

View File

@ -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;
};
/**

View File

@ -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);
};
/**

View File

@ -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;

View File

@ -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:

View File

@ -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;
};
/**