script: classify.
This commit is contained in:
parent
91ee6077b1
commit
51e3341252
@ -484,7 +484,7 @@ exports.isHashType = function isHashType(sig) {
|
|||||||
|
|
||||||
const type = sig[sig.length - 1] & ~exports.hashType.ANYONECANPAY;
|
const type = sig[sig.length - 1] & ~exports.hashType.ANYONECANPAY;
|
||||||
|
|
||||||
if (!(type >= exports.hashType.ALL && type <= exports.hashType.SINGLE))
|
if (type < exports.hashType.ALL || type > exports.hashType.SINGLE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
1150
lib/script/opcode.js
1150
lib/script/opcode.js
File diff suppressed because it is too large
Load Diff
@ -13,91 +13,92 @@ const scriptTypes = common.types;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Witness Program
|
* Witness Program
|
||||||
* @constructor
|
|
||||||
* @alias module:script.Program
|
* @alias module:script.Program
|
||||||
* @param {Number} version
|
|
||||||
* @param {Buffer} data
|
|
||||||
* @property {Number} version - Ranges from 0 to 16.
|
* @property {Number} version - Ranges from 0 to 16.
|
||||||
* @property {String|null} type - Null if malformed. `unknown` if unknown
|
* @property {String|null} type - Null if malformed.
|
||||||
* version (treated as anyone-can-spend). Otherwise one of `witnesspubkeyhash`
|
|
||||||
* or `witnessscripthash`.
|
|
||||||
* @property {Buffer} data - The hash (for now).
|
* @property {Buffer} data - The hash (for now).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Program(version, data) {
|
class Program {
|
||||||
if (!(this instanceof Program))
|
/**
|
||||||
return new Program(version, data);
|
* Create a witness program.
|
||||||
|
* @constructor
|
||||||
|
* @param {Number} version
|
||||||
|
* @param {Buffer} data
|
||||||
|
*/
|
||||||
|
|
||||||
assert((version & 0xff) === version);
|
constructor(version, data) {
|
||||||
assert(version >= 0 && version <= 16);
|
assert((version & 0xff) === version);
|
||||||
assert(Buffer.isBuffer(data));
|
assert(version >= 0 && version <= 16);
|
||||||
assert(data.length >= 2 && data.length <= 40);
|
assert(Buffer.isBuffer(data));
|
||||||
|
assert(data.length >= 2 && data.length <= 40);
|
||||||
|
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the witness program type.
|
||||||
|
* @returns {ScriptType}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getType() {
|
||||||
|
if (this.version === 0) {
|
||||||
|
if (this.data.length === 20)
|
||||||
|
return scriptTypes.WITNESSPUBKEYHASH;
|
||||||
|
|
||||||
|
if (this.data.length === 32)
|
||||||
|
return scriptTypes.WITNESSSCRIPTHASH;
|
||||||
|
|
||||||
|
// Fail on bad version=0
|
||||||
|
return scriptTypes.WITNESSMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.version === 1) {
|
||||||
|
if (this.data.length === 32)
|
||||||
|
return scriptTypes.WITNESSMASTHASH;
|
||||||
|
|
||||||
|
// Fail on bad version=1
|
||||||
|
return scriptTypes.WITNESSMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No interpretation of script (anyone can spend)
|
||||||
|
return scriptTypes.NONSTANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the program is either
|
||||||
|
* an unknown version or malformed.
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
isUnknown() {
|
||||||
|
const type = this.getType();
|
||||||
|
return type === scriptTypes.WITNESSMALFORMED
|
||||||
|
|| type === scriptTypes.NONSTANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the program is malformed.
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
isMalformed() {
|
||||||
|
return this.getType() === scriptTypes.WITNESSMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspect the program.
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
inspect() {
|
||||||
|
const data = this.data.toString('hex');
|
||||||
|
const type = common.typesByVal[this.getType()].toLowerCase();
|
||||||
|
return `<Program: version=${this.version} data=${data} type=${type}>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the witness program type.
|
|
||||||
* @returns {ScriptType}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Program.prototype.getType = function getType() {
|
|
||||||
if (this.version === 0) {
|
|
||||||
if (this.data.length === 20)
|
|
||||||
return scriptTypes.WITNESSPUBKEYHASH;
|
|
||||||
|
|
||||||
if (this.data.length === 32)
|
|
||||||
return scriptTypes.WITNESSSCRIPTHASH;
|
|
||||||
|
|
||||||
// Fail on bad version=0
|
|
||||||
return scriptTypes.WITNESSMALFORMED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.version === 1) {
|
|
||||||
if (this.data.length === 32)
|
|
||||||
return scriptTypes.WITNESSMASTHASH;
|
|
||||||
|
|
||||||
// Fail on bad version=1
|
|
||||||
return scriptTypes.WITNESSMALFORMED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No interpretation of script (anyone can spend)
|
|
||||||
return scriptTypes.NONSTANDARD;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the program is either
|
|
||||||
* an unknown version or malformed.
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Program.prototype.isUnknown = function isUnknown() {
|
|
||||||
const type = this.getType();
|
|
||||||
return type === scriptTypes.WITNESSMALFORMED
|
|
||||||
|| type === scriptTypes.NONSTANDARD;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the program is malformed.
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Program.prototype.isMalformed = function isMalformed() {
|
|
||||||
return this.getType() === scriptTypes.WITNESSMALFORMED;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inspect the program.
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Program.prototype.inspect = function inspect() {
|
|
||||||
const data = this.data.toString('hex');
|
|
||||||
const type = common.typesByVal[this.getType()].toLowerCase();
|
|
||||||
return `<Program: version=${this.version} data=${data} type=${type}>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
6843
lib/script/script.js
6843
lib/script/script.js
File diff suppressed because it is too large
Load Diff
@ -7,44 +7,50 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Script Error
|
||||||
* An error thrown from the scripting system,
|
* An error thrown from the scripting system,
|
||||||
* potentially pertaining to Script execution.
|
* potentially pertaining to Script execution.
|
||||||
* @alias module:script.ScriptError
|
* @alias module:script.ScriptError
|
||||||
* @constructor
|
|
||||||
* @extends Error
|
* @extends Error
|
||||||
* @param {String} code - Error code.
|
|
||||||
* @param {Opcode} op - Opcode.
|
|
||||||
* @param {Number?} ip - Instruction pointer.
|
|
||||||
* @property {String} message - Error message.
|
* @property {String} message - Error message.
|
||||||
* @property {String} code - Original code passed in.
|
* @property {String} code - Original code passed in.
|
||||||
* @property {Number} op - Opcode.
|
* @property {Number} op - Opcode.
|
||||||
* @property {Number} ip - Instruction pointer.
|
* @property {Number} ip - Instruction pointer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function ScriptError(code, op, ip) {
|
class ScriptError extends Error {
|
||||||
if (!(this instanceof ScriptError))
|
/**
|
||||||
return new ScriptError(code, op, ip);
|
* Create an error.
|
||||||
|
* @constructor
|
||||||
|
* @param {String} code - Error code.
|
||||||
|
* @param {Opcode} op - Opcode.
|
||||||
|
* @param {Number?} ip - Instruction pointer.
|
||||||
|
*/
|
||||||
|
|
||||||
Error.call(this);
|
constructor(code, op, ip) {
|
||||||
|
super();
|
||||||
|
|
||||||
this.type = 'ScriptError';
|
this.type = 'ScriptError';
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.message = code;
|
this.message = code;
|
||||||
this.op = -1;
|
this.op = -1;
|
||||||
this.ip = -1;
|
this.ip = -1;
|
||||||
|
|
||||||
if (typeof op === 'string') {
|
if (typeof op === 'string') {
|
||||||
this.message = op;
|
this.message = op;
|
||||||
} else if (op) {
|
} else if (op) {
|
||||||
this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`;
|
this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`;
|
||||||
this.op = op.value;
|
this.op = op.value;
|
||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Error.captureStackTrace)
|
||||||
|
Error.captureStackTrace(this, ScriptError);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Error.captureStackTrace)
|
/*
|
||||||
Error.captureStackTrace(this, ScriptError);
|
* Expose
|
||||||
};
|
*/
|
||||||
|
|
||||||
Object.setPrototypeOf(ScriptError.prototype, Error.prototype);
|
|
||||||
|
|
||||||
module.exports = ScriptError;
|
module.exports = ScriptError;
|
||||||
|
|||||||
@ -20,237 +20,237 @@ const EMPTY_ARRAY = Buffer.alloc(0);
|
|||||||
* Script Number
|
* Script Number
|
||||||
* @see https://github.com/chjj/n64
|
* @see https://github.com/chjj/n64
|
||||||
* @alias module:script.ScriptNum
|
* @alias module:script.ScriptNum
|
||||||
* @constructor
|
|
||||||
* @param {(Number|String|Buffer|Object)?} num
|
|
||||||
* @param {(String|Number)?} base
|
|
||||||
* @property {Number} hi
|
* @property {Number} hi
|
||||||
* @property {Number} lo
|
* @property {Number} lo
|
||||||
* @property {Number} sign
|
* @property {Number} sign
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function ScriptNum(num, base) {
|
class ScriptNum extends I64 {
|
||||||
if (!(this instanceof ScriptNum))
|
/**
|
||||||
return new ScriptNum(num, base);
|
* Create a script number.
|
||||||
|
* @constructor
|
||||||
|
* @param {(Number|String|Buffer|Object)?} num
|
||||||
|
* @param {(String|Number)?} base
|
||||||
|
*/
|
||||||
|
|
||||||
I64.call(this, num, base);
|
constructor(num, base) {
|
||||||
}
|
super(num, base);
|
||||||
|
|
||||||
Object.setPrototypeOf(ScriptNum, I64);
|
|
||||||
Object.setPrototypeOf(ScriptNum.prototype, I64.prototype);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cast to int32.
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.prototype.getInt = function getInt() {
|
|
||||||
if (this.lt(I64.INT32_MIN))
|
|
||||||
return I64.LONG_MIN;
|
|
||||||
|
|
||||||
if (this.gt(I64.INT32_MAX))
|
|
||||||
return I64.LONG_MAX;
|
|
||||||
|
|
||||||
return this.toInt();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize script number.
|
|
||||||
* @returns {Buffer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.prototype.toRaw = function toRaw() {
|
|
||||||
let num = this;
|
|
||||||
|
|
||||||
// Zeroes are always empty arrays.
|
|
||||||
if (num.isZero())
|
|
||||||
return EMPTY_ARRAY;
|
|
||||||
|
|
||||||
// Need to append sign bit.
|
|
||||||
let neg = false;
|
|
||||||
if (num.isNeg()) {
|
|
||||||
num = num.neg();
|
|
||||||
neg = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate size.
|
/**
|
||||||
const size = num.byteLength();
|
* Cast to int32.
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
let offset = 0;
|
getInt() {
|
||||||
|
if (this.lt(I64.INT32_MIN))
|
||||||
|
return I64.LONG_MIN;
|
||||||
|
|
||||||
if (num.testn((size * 8) - 1))
|
if (this.gt(I64.INT32_MAX))
|
||||||
offset = 1;
|
return I64.LONG_MAX;
|
||||||
|
|
||||||
// Write number.
|
return this.toInt();
|
||||||
const data = Buffer.allocUnsafe(size + offset);
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 8:
|
|
||||||
data[7] = (num.hi >>> 24) & 0xff;
|
|
||||||
case 7:
|
|
||||||
data[6] = (num.hi >> 16) & 0xff;
|
|
||||||
case 6:
|
|
||||||
data[5] = (num.hi >> 8) & 0xff;
|
|
||||||
case 5:
|
|
||||||
data[4] = num.hi & 0xff;
|
|
||||||
case 4:
|
|
||||||
data[3] = (num.lo >>> 24) & 0xff;
|
|
||||||
case 3:
|
|
||||||
data[2] = (num.lo >> 16) & 0xff;
|
|
||||||
case 2:
|
|
||||||
data[1] = (num.lo >> 8) & 0xff;
|
|
||||||
case 1:
|
|
||||||
data[0] = num.lo & 0xff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append sign bit.
|
/**
|
||||||
if (data[size - 1] & 0x80) {
|
* Serialize script number.
|
||||||
assert(offset === 1);
|
* @returns {Buffer}
|
||||||
assert(data.length === size + offset);
|
*/
|
||||||
data[size] = neg ? 0x80 : 0;
|
|
||||||
} else if (neg) {
|
toRaw() {
|
||||||
assert(offset === 0);
|
let num = this;
|
||||||
assert(data.length === size);
|
|
||||||
data[size - 1] |= 0x80;
|
// Zeroes are always empty arrays.
|
||||||
} else {
|
if (num.isZero())
|
||||||
assert(offset === 0);
|
return EMPTY_ARRAY;
|
||||||
assert(data.length === size);
|
|
||||||
|
// Need to append sign bit.
|
||||||
|
let neg = false;
|
||||||
|
if (num.isNeg()) {
|
||||||
|
num = num.neg();
|
||||||
|
neg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate size.
|
||||||
|
const size = num.byteLength();
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
if (num.testn((size * 8) - 1))
|
||||||
|
offset = 1;
|
||||||
|
|
||||||
|
// Write number.
|
||||||
|
const data = Buffer.allocUnsafe(size + offset);
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 8:
|
||||||
|
data[7] = (num.hi >>> 24) & 0xff;
|
||||||
|
case 7:
|
||||||
|
data[6] = (num.hi >> 16) & 0xff;
|
||||||
|
case 6:
|
||||||
|
data[5] = (num.hi >> 8) & 0xff;
|
||||||
|
case 5:
|
||||||
|
data[4] = num.hi & 0xff;
|
||||||
|
case 4:
|
||||||
|
data[3] = (num.lo >>> 24) & 0xff;
|
||||||
|
case 3:
|
||||||
|
data[2] = (num.lo >> 16) & 0xff;
|
||||||
|
case 2:
|
||||||
|
data[1] = (num.lo >> 8) & 0xff;
|
||||||
|
case 1:
|
||||||
|
data[0] = num.lo & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append sign bit.
|
||||||
|
if (data[size - 1] & 0x80) {
|
||||||
|
assert(offset === 1);
|
||||||
|
assert(data.length === size + offset);
|
||||||
|
data[size] = neg ? 0x80 : 0;
|
||||||
|
} else if (neg) {
|
||||||
|
assert(offset === 0);
|
||||||
|
assert(data.length === size);
|
||||||
|
data[size - 1] |= 0x80;
|
||||||
|
} else {
|
||||||
|
assert(offset === 0);
|
||||||
|
assert(data.length === size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
/**
|
||||||
};
|
* Instantiate script number from serialized data.
|
||||||
|
* @private
|
||||||
|
* @param {Buffer} data
|
||||||
|
* @returns {ScriptNum}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
fromRaw(data) {
|
||||||
* Instantiate script number from serialized data.
|
assert(Buffer.isBuffer(data));
|
||||||
* @private
|
|
||||||
* @param {Buffer} data
|
|
||||||
* @returns {ScriptNum}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.prototype.fromRaw = function fromRaw(data) {
|
// Empty arrays are always zero.
|
||||||
assert(Buffer.isBuffer(data));
|
if (data.length === 0)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
// Read number (9 bytes max).
|
||||||
|
switch (data.length) {
|
||||||
|
case 8:
|
||||||
|
this.hi |= data[7] << 24;
|
||||||
|
case 7:
|
||||||
|
this.hi |= data[6] << 16;
|
||||||
|
case 6:
|
||||||
|
this.hi |= data[5] << 8;
|
||||||
|
case 5:
|
||||||
|
this.hi |= data[4];
|
||||||
|
case 4:
|
||||||
|
this.lo |= data[3] << 24;
|
||||||
|
case 3:
|
||||||
|
this.lo |= data[2] << 16;
|
||||||
|
case 2:
|
||||||
|
this.lo |= data[1] << 8;
|
||||||
|
case 1:
|
||||||
|
this.lo |= data[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (let i = 0; i < data.length; i++)
|
||||||
|
this.orb(i, data[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove high bit and flip sign.
|
||||||
|
if (data[data.length - 1] & 0x80) {
|
||||||
|
this.setn((data.length * 8) - 1, 0);
|
||||||
|
this.ineg();
|
||||||
|
}
|
||||||
|
|
||||||
// Empty arrays are always zero.
|
|
||||||
if (data.length === 0)
|
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
// Read number (9 bytes max).
|
|
||||||
switch (data.length) {
|
|
||||||
case 8:
|
|
||||||
this.hi |= data[7] << 24;
|
|
||||||
case 7:
|
|
||||||
this.hi |= data[6] << 16;
|
|
||||||
case 6:
|
|
||||||
this.hi |= data[5] << 8;
|
|
||||||
case 5:
|
|
||||||
this.hi |= data[4];
|
|
||||||
case 4:
|
|
||||||
this.lo |= data[3] << 24;
|
|
||||||
case 3:
|
|
||||||
this.lo |= data[2] << 16;
|
|
||||||
case 2:
|
|
||||||
this.lo |= data[1] << 8;
|
|
||||||
case 1:
|
|
||||||
this.lo |= data[0];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (let i = 0; i < data.length; i++)
|
|
||||||
this.orb(i, data[i]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove high bit and flip sign.
|
/**
|
||||||
if (data[data.length - 1] & 0x80) {
|
* Serialize script number.
|
||||||
this.setn((data.length * 8) - 1, 0);
|
* @returns {Buffer}
|
||||||
this.ineg();
|
*/
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
return this.toRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
/**
|
||||||
};
|
* Decode and verify script number.
|
||||||
|
* @private
|
||||||
|
* @param {Buffer} data
|
||||||
|
* @param {Boolean?} minimal - Require minimal encoding.
|
||||||
|
* @param {Number?} limit - Size limit.
|
||||||
|
* @returns {ScriptNum}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
decode(data, minimal, limit) {
|
||||||
* Serialize script number.
|
assert(Buffer.isBuffer(data));
|
||||||
* @returns {Buffer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.prototype.encode = function encode() {
|
if (limit != null && data.length > limit)
|
||||||
return this.toRaw();
|
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
if (minimal && !ScriptNum.isMinimal(data))
|
||||||
* Decode and verify script number.
|
throw new ScriptError('UNKNOWN_ERROR', 'Non-minimal script number.');
|
||||||
* @private
|
|
||||||
* @param {Buffer} data
|
|
||||||
* @param {Boolean?} minimal - Require minimal encoding.
|
|
||||||
* @param {Number?} limit - Size limit.
|
|
||||||
* @returns {ScriptNum}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.prototype.decode = function decode(data, minimal, limit) {
|
return this.fromRaw(data);
|
||||||
assert(Buffer.isBuffer(data));
|
}
|
||||||
|
|
||||||
if (limit != null && data.length > limit)
|
/**
|
||||||
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
|
* Inspect script number.
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
|
||||||
if (minimal && !ScriptNum.isMinimal(data))
|
inspect() {
|
||||||
throw new ScriptError('UNKNOWN_ERROR', 'Non-minimal script number.');
|
return `<ScriptNum: ${this.toString(10)}>`;
|
||||||
|
}
|
||||||
|
|
||||||
return this.fromRaw(data);
|
/**
|
||||||
};
|
* Test wether a serialized script
|
||||||
|
* number is in its most minimal form.
|
||||||
|
* @param {Buffer} data
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static isMinimal(data) {
|
||||||
* Inspect script number.
|
assert(Buffer.isBuffer(data));
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.prototype.inspect = function inspect() {
|
if (data.length === 0)
|
||||||
return `<ScriptNum: ${this.toString(10)}>`;
|
return true;
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
if ((data[data.length - 1] & 0x7f) === 0) {
|
||||||
* Test wether a serialized script
|
if (data.length === 1)
|
||||||
* number is in its most minimal form.
|
return false;
|
||||||
* @param {Buffer} data
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.isMinimal = function isMinimal(data) {
|
if ((data[data.length - 2] & 0x80) === 0)
|
||||||
assert(Buffer.isBuffer(data));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.length === 0)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if ((data[data.length - 1] & 0x7f) === 0) {
|
|
||||||
if (data.length === 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ((data[data.length - 2] & 0x80) === 0)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
/**
|
||||||
};
|
* Decode and verify script number.
|
||||||
|
* @param {Buffer} data
|
||||||
|
* @param {Boolean?} minimal - Require minimal encoding.
|
||||||
|
* @param {Number?} limit - Size limit.
|
||||||
|
* @returns {ScriptNum}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static decode(data, minimal, limit) {
|
||||||
* Decode and verify script number.
|
return new this().decode(data, minimal, limit);
|
||||||
* @param {Buffer} data
|
}
|
||||||
* @param {Boolean?} minimal - Require minimal encoding.
|
|
||||||
* @param {Number?} limit - Size limit.
|
|
||||||
* @returns {ScriptNum}
|
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.decode = function decode(data, minimal, limit) {
|
/**
|
||||||
return new ScriptNum().decode(data, minimal, limit);
|
* Test whether object is a script number.
|
||||||
};
|
* @param {Object} obj
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static isScriptNum(obj) {
|
||||||
* Test whether object is a script number.
|
return obj instanceof ScriptNum;
|
||||||
* @param {Object} obj
|
}
|
||||||
* @returns {Boolean}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
ScriptNum.isScriptNum = function isScriptNum(obj) {
|
|
||||||
return obj instanceof ScriptNum;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
|
|||||||
@ -12,135 +12,144 @@ const secp256k1 = require('bcrypto/lib/secp256k1');
|
|||||||
/**
|
/**
|
||||||
* Signature cache.
|
* Signature cache.
|
||||||
* @alias module:script.SigCache
|
* @alias module:script.SigCache
|
||||||
* @constructor
|
|
||||||
* @param {Number} [size=10000]
|
|
||||||
* @property {Number} size
|
* @property {Number} size
|
||||||
* @property {Hash[]} keys
|
* @property {Hash[]} keys
|
||||||
* @property {Object} valid
|
* @property {Object} valid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function SigCache(size) {
|
class SigCache {
|
||||||
if (!(this instanceof SigCache))
|
/**
|
||||||
return new SigCache(size);
|
* Create a signature cache.
|
||||||
|
* @constructor
|
||||||
|
* @param {Number} [size=10000]
|
||||||
|
*/
|
||||||
|
|
||||||
if (size == null)
|
constructor(size) {
|
||||||
size = 10000;
|
if (size == null)
|
||||||
|
size = 10000;
|
||||||
|
|
||||||
assert((size >>> 0) === size);
|
assert((size >>> 0) === size);
|
||||||
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.keys = [];
|
this.keys = [];
|
||||||
this.valid = new Map();
|
this.valid = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the sigcache.
|
||||||
|
* @param {Number} size
|
||||||
|
*/
|
||||||
|
|
||||||
|
resize(size) {
|
||||||
|
assert((size >>> 0) === size);
|
||||||
|
|
||||||
|
this.size = size;
|
||||||
|
this.keys.length = 0;
|
||||||
|
this.valid.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to the sigcache.
|
||||||
|
* Potentially evict a random member.
|
||||||
|
* @param {Hash} hash - Sig hash.
|
||||||
|
* @param {Buffer} sig
|
||||||
|
* @param {Buffer} key
|
||||||
|
*/
|
||||||
|
|
||||||
|
add(hash, sig, key) {
|
||||||
|
if (this.size === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.valid.set(hash, new SigCacheEntry(sig, key));
|
||||||
|
|
||||||
|
if (this.keys.length >= this.size) {
|
||||||
|
const i = Math.floor(Math.random() * this.keys.length);
|
||||||
|
const k = this.keys[i];
|
||||||
|
this.valid.delete(k);
|
||||||
|
this.keys[i] = hash;
|
||||||
|
} else {
|
||||||
|
this.keys.push(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the sig exists.
|
||||||
|
* @param {Hash} hash - Sig hash.
|
||||||
|
* @param {Buffer} sig
|
||||||
|
* @param {Buffer} key
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
has(hash, sig, key) {
|
||||||
|
const entry = this.valid.get(hash);
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return entry.equals(sig, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a signature, testing
|
||||||
|
* it against the cache first.
|
||||||
|
* @param {Buffer} msg
|
||||||
|
* @param {Buffer} sig
|
||||||
|
* @param {Buffer} key
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
verify(msg, sig, key) {
|
||||||
|
if (this.size === 0)
|
||||||
|
return secp256k1.verify(msg, sig, key);
|
||||||
|
|
||||||
|
const hash = msg.toString('hex');
|
||||||
|
|
||||||
|
if (this.has(hash, sig, key))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const result = secp256k1.verify(msg, sig, key);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.add(hash, sig, key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize the sigcache.
|
* Signature Cache Entry
|
||||||
* @param {Number} size
|
|
||||||
*/
|
|
||||||
|
|
||||||
SigCache.prototype.resize = function resize(size) {
|
|
||||||
assert((size >>> 0) === size);
|
|
||||||
|
|
||||||
this.size = size;
|
|
||||||
this.keys.length = 0;
|
|
||||||
this.valid.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add item to the sigcache.
|
|
||||||
* Potentially evict a random member.
|
|
||||||
* @param {Hash} hash - Sig hash.
|
|
||||||
* @param {Buffer} sig
|
|
||||||
* @param {Buffer} key
|
|
||||||
*/
|
|
||||||
|
|
||||||
SigCache.prototype.add = function add(hash, sig, key) {
|
|
||||||
if (this.size === 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.valid.set(hash, new SigCacheEntry(sig, key));
|
|
||||||
|
|
||||||
if (this.keys.length >= this.size) {
|
|
||||||
const i = Math.floor(Math.random() * this.keys.length);
|
|
||||||
const k = this.keys[i];
|
|
||||||
this.valid.delete(k);
|
|
||||||
this.keys[i] = hash;
|
|
||||||
} else {
|
|
||||||
this.keys.push(hash);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the sig exists.
|
|
||||||
* @param {Hash} hash - Sig hash.
|
|
||||||
* @param {Buffer} sig
|
|
||||||
* @param {Buffer} key
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
SigCache.prototype.has = function has(hash, sig, key) {
|
|
||||||
const entry = this.valid.get(hash);
|
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return entry.equals(sig, key);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify a signature, testing
|
|
||||||
* it against the cache first.
|
|
||||||
* @param {Buffer} msg
|
|
||||||
* @param {Buffer} sig
|
|
||||||
* @param {Buffer} key
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
SigCache.prototype.verify = function verify(msg, sig, key) {
|
|
||||||
if (this.size === 0)
|
|
||||||
return secp256k1.verify(msg, sig, key);
|
|
||||||
|
|
||||||
const hash = msg.toString('hex');
|
|
||||||
|
|
||||||
if (this.has(hash, sig, key))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const result = secp256k1.verify(msg, sig, key);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
this.add(hash, sig, key);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature cache entry.
|
|
||||||
* @constructor
|
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {Buffer} sig
|
|
||||||
* @param {Buffer} key
|
|
||||||
* @property {Buffer} sig
|
* @property {Buffer} sig
|
||||||
* @property {Buffer} key
|
* @property {Buffer} key
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function SigCacheEntry(sig, key) {
|
class SigCacheEntry {
|
||||||
this.sig = Buffer.from(sig);
|
/**
|
||||||
this.key = Buffer.from(key);
|
* Create a cache entry.
|
||||||
|
* @constructor
|
||||||
|
* @param {Buffer} sig
|
||||||
|
* @param {Buffer} key
|
||||||
|
*/
|
||||||
|
|
||||||
|
constructor(sig, key) {
|
||||||
|
this.sig = Buffer.from(sig);
|
||||||
|
this.key = Buffer.from(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare an entry to a sig and key.
|
||||||
|
* @param {Buffer} sig
|
||||||
|
* @param {Buffer} key
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
equals(sig, key) {
|
||||||
|
return this.sig.equals(sig) && this.key.equals(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare an entry to a sig and key.
|
|
||||||
* @param {Buffer} sig
|
|
||||||
* @param {Buffer} key
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
SigCacheEntry.prototype.equals = function equals(sig, key) {
|
|
||||||
return this.sig.equals(sig) && this.key.equals(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
1070
lib/script/stack.js
1070
lib/script/stack.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user