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;
|
||||
|
||||
if (!(type >= exports.hashType.ALL && type <= exports.hashType.SINGLE))
|
||||
if (type < exports.hashType.ALL || type > exports.hashType.SINGLE)
|
||||
return false;
|
||||
|
||||
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
|
||||
* @constructor
|
||||
* @alias module:script.Program
|
||||
* @param {Number} version
|
||||
* @param {Buffer} data
|
||||
* @property {Number} version - Ranges from 0 to 16.
|
||||
* @property {String|null} type - Null if malformed. `unknown` if unknown
|
||||
* version (treated as anyone-can-spend). Otherwise one of `witnesspubkeyhash`
|
||||
* or `witnessscripthash`.
|
||||
* @property {String|null} type - Null if malformed.
|
||||
* @property {Buffer} data - The hash (for now).
|
||||
*/
|
||||
|
||||
function Program(version, data) {
|
||||
if (!(this instanceof Program))
|
||||
return new Program(version, data);
|
||||
class Program {
|
||||
/**
|
||||
* Create a witness program.
|
||||
* @constructor
|
||||
* @param {Number} version
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
assert((version & 0xff) === version);
|
||||
assert(version >= 0 && version <= 16);
|
||||
assert(Buffer.isBuffer(data));
|
||||
assert(data.length >= 2 && data.length <= 40);
|
||||
constructor(version, data) {
|
||||
assert((version & 0xff) === version);
|
||||
assert(version >= 0 && version <= 16);
|
||||
assert(Buffer.isBuffer(data));
|
||||
assert(data.length >= 2 && data.length <= 40);
|
||||
|
||||
this.version = version;
|
||||
this.data = data;
|
||||
this.version = version;
|
||||
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
|
||||
*/
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* Script Error
|
||||
* An error thrown from the scripting system,
|
||||
* potentially pertaining to Script execution.
|
||||
* @alias module:script.ScriptError
|
||||
* @constructor
|
||||
* @extends Error
|
||||
* @param {String} code - Error code.
|
||||
* @param {Opcode} op - Opcode.
|
||||
* @param {Number?} ip - Instruction pointer.
|
||||
* @property {String} message - Error message.
|
||||
* @property {String} code - Original code passed in.
|
||||
* @property {Number} op - Opcode.
|
||||
* @property {Number} ip - Instruction pointer.
|
||||
*/
|
||||
|
||||
function ScriptError(code, op, ip) {
|
||||
if (!(this instanceof ScriptError))
|
||||
return new ScriptError(code, op, ip);
|
||||
class ScriptError extends Error {
|
||||
/**
|
||||
* 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.code = code;
|
||||
this.message = code;
|
||||
this.op = -1;
|
||||
this.ip = -1;
|
||||
this.type = 'ScriptError';
|
||||
this.code = code;
|
||||
this.message = code;
|
||||
this.op = -1;
|
||||
this.ip = -1;
|
||||
|
||||
if (typeof op === 'string') {
|
||||
this.message = op;
|
||||
} else if (op) {
|
||||
this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`;
|
||||
this.op = op.value;
|
||||
this.ip = ip;
|
||||
if (typeof op === 'string') {
|
||||
this.message = op;
|
||||
} else if (op) {
|
||||
this.message = `${code} (op=${op.toSymbol()}, ip=${ip})`;
|
||||
this.op = op.value;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, ScriptError);
|
||||
}
|
||||
}
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, ScriptError);
|
||||
};
|
||||
|
||||
Object.setPrototypeOf(ScriptError.prototype, Error.prototype);
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = ScriptError;
|
||||
|
||||
@ -20,237 +20,237 @@ const EMPTY_ARRAY = Buffer.alloc(0);
|
||||
* Script Number
|
||||
* @see https://github.com/chjj/n64
|
||||
* @alias module:script.ScriptNum
|
||||
* @constructor
|
||||
* @param {(Number|String|Buffer|Object)?} num
|
||||
* @param {(String|Number)?} base
|
||||
* @property {Number} hi
|
||||
* @property {Number} lo
|
||||
* @property {Number} sign
|
||||
*/
|
||||
|
||||
function ScriptNum(num, base) {
|
||||
if (!(this instanceof ScriptNum))
|
||||
return new ScriptNum(num, base);
|
||||
class ScriptNum extends I64 {
|
||||
/**
|
||||
* Create a script number.
|
||||
* @constructor
|
||||
* @param {(Number|String|Buffer|Object)?} num
|
||||
* @param {(String|Number)?} base
|
||||
*/
|
||||
|
||||
I64.call(this, 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;
|
||||
constructor(num, base) {
|
||||
super(num, base);
|
||||
}
|
||||
|
||||
// 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))
|
||||
offset = 1;
|
||||
if (this.gt(I64.INT32_MAX))
|
||||
return I64.LONG_MAX;
|
||||
|
||||
// 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;
|
||||
return this.toInt();
|
||||
}
|
||||
|
||||
// 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);
|
||||
/**
|
||||
* Serialize script number.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
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();
|
||||
|
||||
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}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Instantiate script number from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @returns {ScriptNum}
|
||||
*/
|
||||
fromRaw(data) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
|
||||
ScriptNum.prototype.fromRaw = function fromRaw(data) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
// Empty arrays are always zero.
|
||||
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;
|
||||
|
||||
// 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();
|
||||
/**
|
||||
* Serialize script number.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
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}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serialize script number.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
decode(data, minimal, limit) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
|
||||
ScriptNum.prototype.encode = function encode() {
|
||||
return this.toRaw();
|
||||
};
|
||||
if (limit != null && data.length > limit)
|
||||
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
|
||||
|
||||
/**
|
||||
* Decode and verify script number.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @param {Boolean?} minimal - Require minimal encoding.
|
||||
* @param {Number?} limit - Size limit.
|
||||
* @returns {ScriptNum}
|
||||
*/
|
||||
if (minimal && !ScriptNum.isMinimal(data))
|
||||
throw new ScriptError('UNKNOWN_ERROR', 'Non-minimal script number.');
|
||||
|
||||
ScriptNum.prototype.decode = function decode(data, minimal, limit) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
return this.fromRaw(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))
|
||||
throw new ScriptError('UNKNOWN_ERROR', 'Non-minimal script number.');
|
||||
inspect() {
|
||||
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}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Inspect script number.
|
||||
* @returns {String}
|
||||
*/
|
||||
static isMinimal(data) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
|
||||
ScriptNum.prototype.inspect = function inspect() {
|
||||
return `<ScriptNum: ${this.toString(10)}>`;
|
||||
};
|
||||
if (data.length === 0)
|
||||
return true;
|
||||
|
||||
/**
|
||||
* Test wether a serialized script
|
||||
* number is in its most minimal form.
|
||||
* @param {Buffer} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
if ((data[data.length - 1] & 0x7f) === 0) {
|
||||
if (data.length === 1)
|
||||
return false;
|
||||
|
||||
ScriptNum.isMinimal = function isMinimal(data) {
|
||||
assert(Buffer.isBuffer(data));
|
||||
if ((data[data.length - 2] & 0x80) === 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.length === 0)
|
||||
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}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return new this().decode(data, minimal, limit);
|
||||
}
|
||||
|
||||
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}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test whether object is a script number.
|
||||
* @param {Object} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
ScriptNum.isScriptNum = function isScriptNum(obj) {
|
||||
return obj instanceof ScriptNum;
|
||||
};
|
||||
static isScriptNum(obj) {
|
||||
return obj instanceof ScriptNum;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
|
||||
@ -12,135 +12,144 @@ const secp256k1 = require('bcrypto/lib/secp256k1');
|
||||
/**
|
||||
* Signature cache.
|
||||
* @alias module:script.SigCache
|
||||
* @constructor
|
||||
* @param {Number} [size=10000]
|
||||
* @property {Number} size
|
||||
* @property {Hash[]} keys
|
||||
* @property {Object} valid
|
||||
*/
|
||||
|
||||
function SigCache(size) {
|
||||
if (!(this instanceof SigCache))
|
||||
return new SigCache(size);
|
||||
class SigCache {
|
||||
/**
|
||||
* Create a signature cache.
|
||||
* @constructor
|
||||
* @param {Number} [size=10000]
|
||||
*/
|
||||
|
||||
if (size == null)
|
||||
size = 10000;
|
||||
constructor(size) {
|
||||
if (size == null)
|
||||
size = 10000;
|
||||
|
||||
assert((size >>> 0) === size);
|
||||
assert((size >>> 0) === size);
|
||||
|
||||
this.size = size;
|
||||
this.keys = [];
|
||||
this.valid = new Map();
|
||||
this.size = size;
|
||||
this.keys = [];
|
||||
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.
|
||||
* @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
|
||||
* Signature Cache Entry
|
||||
* @ignore
|
||||
* @param {Buffer} sig
|
||||
* @param {Buffer} key
|
||||
* @property {Buffer} sig
|
||||
* @property {Buffer} key
|
||||
*/
|
||||
|
||||
function SigCacheEntry(sig, key) {
|
||||
this.sig = Buffer.from(sig);
|
||||
this.key = Buffer.from(key);
|
||||
class SigCacheEntry {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
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