|
|
|
|
@ -35,15 +35,25 @@ var STACK_NEGATE = new Buffer([0xff]);
|
|
|
|
|
* @constructor
|
|
|
|
|
* @param {Buffer[]|Buffer|NakedWitness} items - Array of
|
|
|
|
|
* stack items or raw witness buffer.
|
|
|
|
|
* @param {Boolean} mutable - Whether the script will
|
|
|
|
|
* be changed in the future.
|
|
|
|
|
* @property {Buffer[]} items
|
|
|
|
|
* @property {Script?} redeem
|
|
|
|
|
* @property {Boolean} mutable
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
function Witness(items) {
|
|
|
|
|
if (items instanceof Witness)
|
|
|
|
|
function Witness(items, mutable) {
|
|
|
|
|
if (items instanceof Witness) {
|
|
|
|
|
items.mutable = !!mutable;
|
|
|
|
|
items.redeem = null;
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(this instanceof Witness))
|
|
|
|
|
return new Witness(items);
|
|
|
|
|
|
|
|
|
|
this.mutable = !!mutable;
|
|
|
|
|
|
|
|
|
|
if (!items)
|
|
|
|
|
items = [];
|
|
|
|
|
|
|
|
|
|
@ -167,12 +177,25 @@ Witness.prototype.isUnknownInput = function isUnknownInput() {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Witness.prototype.getRedeem = function getRedeem() {
|
|
|
|
|
if (this.mutable)
|
|
|
|
|
return Script.getRedeem(this.items);
|
|
|
|
|
|
|
|
|
|
if (!this.redeem)
|
|
|
|
|
this.redeem = Script.getRedeem(this.items);
|
|
|
|
|
|
|
|
|
|
return this.redeem;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find a data element in a witness.
|
|
|
|
|
* @param {Buffer} data - Data element to match against.
|
|
|
|
|
* @returns {Number} Index (`-1` if not present).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Witness.prototype.indexOf = function indexOf(data) {
|
|
|
|
|
return utils.indexOf(this.items, data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse a formatted script
|
|
|
|
|
* string into a witness object. _Must_
|
|
|
|
|
@ -761,18 +784,27 @@ Stack.isStack = function isStack(obj) {
|
|
|
|
|
* @constructor
|
|
|
|
|
* @param {Buffer|Array|Object|NakedScript} code - Array
|
|
|
|
|
* of script code or a serialized script Buffer.
|
|
|
|
|
* @param {Boolean} mutable - Whether the script will
|
|
|
|
|
* be changed in the future.
|
|
|
|
|
* @property {Array} code - Script code.
|
|
|
|
|
* @property {Buffer} raw - Serialized script.
|
|
|
|
|
* @property {Script} redeem - Redeem script.
|
|
|
|
|
* @property {Buffer?} raw - Serialized script.
|
|
|
|
|
* @property {Script?} redeem - Redeem script.
|
|
|
|
|
* @property {Boolean} mutable
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
function Script(code) {
|
|
|
|
|
if (code instanceof Script)
|
|
|
|
|
function Script(code, mutable) {
|
|
|
|
|
if (code instanceof Script) {
|
|
|
|
|
code.mutable = !!mutable;
|
|
|
|
|
code.raw = null;
|
|
|
|
|
code.redeem = null;
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(this instanceof Script))
|
|
|
|
|
return new Script(code);
|
|
|
|
|
|
|
|
|
|
this.mutable = !!mutable;
|
|
|
|
|
|
|
|
|
|
if (Buffer.isBuffer(code)) {
|
|
|
|
|
this.raw = code;
|
|
|
|
|
this.code = Script.decode(code);
|
|
|
|
|
@ -791,6 +823,9 @@ function Script(code) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.mutable)
|
|
|
|
|
this.raw = null;
|
|
|
|
|
|
|
|
|
|
this.redeem = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -820,8 +855,12 @@ Script.prototype.inspect = function inspect() {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.encode = function encode() {
|
|
|
|
|
if (this.mutable)
|
|
|
|
|
return Script.encode(this.code);
|
|
|
|
|
|
|
|
|
|
if (!this.raw)
|
|
|
|
|
this.raw = Script.encode(this.code);
|
|
|
|
|
|
|
|
|
|
return this.raw;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -920,12 +959,14 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
|
|
|
|
op = this.code[ip];
|
|
|
|
|
|
|
|
|
|
if (Buffer.isBuffer(op)) {
|
|
|
|
|
if (!Script.checkPush(op))
|
|
|
|
|
throw new ScriptError('Pushdata out of range.', op, ip);
|
|
|
|
|
if (op.length > constants.script.MAX_PUSH)
|
|
|
|
|
throw new ScriptError('Push data too large.', op, ip);
|
|
|
|
|
throw new ScriptError('Pushdata too large.', op, ip);
|
|
|
|
|
// Note that minimaldata is not checked
|
|
|
|
|
// on unexecuted branches of code.
|
|
|
|
|
if (stack.negate === 0) {
|
|
|
|
|
if (!Script.checkPush(op, flags))
|
|
|
|
|
if (!Script.checkMinimal(op, flags))
|
|
|
|
|
throw new ScriptError('Push verification failed.', op, ip);
|
|
|
|
|
stack.push(op);
|
|
|
|
|
}
|
|
|
|
|
@ -1366,7 +1407,8 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
|
|
|
|
type = sig[sig.length - 1];
|
|
|
|
|
|
|
|
|
|
subscript = this.getSubscript(lastSep);
|
|
|
|
|
subscript.removeData(sig);
|
|
|
|
|
if (version === 0)
|
|
|
|
|
subscript.removeData(sig);
|
|
|
|
|
|
|
|
|
|
hash = tx.signatureHash(index, subscript, type, version);
|
|
|
|
|
|
|
|
|
|
@ -1418,7 +1460,8 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < m; i++) {
|
|
|
|
|
sig = stack.get(stack.length - 1 - i);
|
|
|
|
|
subscript.removeData(sig);
|
|
|
|
|
if (version === 0)
|
|
|
|
|
subscript.removeData(sig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
succ = 0;
|
|
|
|
|
@ -1680,7 +1723,6 @@ Script.checkSequence = function checkSequence(sequence, tx, i) {
|
|
|
|
|
/**
|
|
|
|
|
* Cast a big number or Buffer to a bool.
|
|
|
|
|
* @see CastToBool
|
|
|
|
|
* @static
|
|
|
|
|
* @param {BN|Buffer} value
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
@ -1707,7 +1749,6 @@ Script.bool = function bool(value) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a CScriptNum.
|
|
|
|
|
* @static
|
|
|
|
|
* @param {Buffer} value
|
|
|
|
|
* @param {Number?} flags - Script standard flags.
|
|
|
|
|
* @param {Number?} size - Max size in bytes.
|
|
|
|
|
@ -1763,7 +1804,6 @@ Script.num = function num(value, flags, size) {
|
|
|
|
|
* Create a script array. Will convert Numbers and big
|
|
|
|
|
* numbers to a little-endian buffer while taking into
|
|
|
|
|
* account negative zero, minimaldata, etc.
|
|
|
|
|
* @static
|
|
|
|
|
* @example
|
|
|
|
|
* assert.deepEqual(Script.array(0), new Buffer([]));
|
|
|
|
|
* assert.deepEqual(Script.array(0xffee), new Buffer([0xee, 0xff]));
|
|
|
|
|
@ -1801,27 +1841,76 @@ Script.array = function(value) {
|
|
|
|
|
* a script's code (used to remove signatures
|
|
|
|
|
* before verification).
|
|
|
|
|
* @param {Buffer} data - Data element to match against.
|
|
|
|
|
* @returns {Number} Total.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.removeData = function removeData(data) {
|
|
|
|
|
for (var i = this.code.length - 1; i >= 0; i--) {
|
|
|
|
|
var total = 0;
|
|
|
|
|
var i;
|
|
|
|
|
|
|
|
|
|
for (i = this.code.length - 1; i >= 0; i--) {
|
|
|
|
|
if (!Buffer.isBuffer(this.code[i]))
|
|
|
|
|
continue;
|
|
|
|
|
if (utils.isEqual(this.code[i], data))
|
|
|
|
|
if (utils.isEqual(this.code[i], data)) {
|
|
|
|
|
this.code.splice(i, 1);
|
|
|
|
|
total++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find a data element in a script.
|
|
|
|
|
* @param {Buffer} data - Data element to match against.
|
|
|
|
|
* @returns {Number} Index (`-1` if not present).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.indexOf = function indexOf(data) {
|
|
|
|
|
return utils.indexOf(this.code, data);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test whether an op is a buffer, also
|
|
|
|
|
* check for buffer underflows.
|
|
|
|
|
* @param {Buffer?} value
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isPush = function isPush(value) {
|
|
|
|
|
return Buffer.isBuffer(value) && Script.checkPush(value);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Perform some range checking on the pushdatas
|
|
|
|
|
* (exactly what GetOp2 does). Note that this
|
|
|
|
|
* _must_ be done during execution, not parsing.
|
|
|
|
|
* @see GetOp2
|
|
|
|
|
* @param {Buffer} value - Pushdata op from script code
|
|
|
|
|
* (must be from a deserialized script).
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.checkPush = function checkPush(value) {
|
|
|
|
|
var pushdata = value.pushdata;
|
|
|
|
|
|
|
|
|
|
if (!pushdata)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// The pushdata size can never
|
|
|
|
|
// be greater than the buffer.
|
|
|
|
|
return pushdata.size === value.length;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check to see if a pushdata Buffer abides by minimaldata.
|
|
|
|
|
* @static
|
|
|
|
|
* @param {Buffer} value - Pushdata op from script code
|
|
|
|
|
* (must be from a deserialized script).
|
|
|
|
|
* @param {Number?} flags
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.checkPush = function checkPush(value, flags) {
|
|
|
|
|
Script.checkMinimal = function checkMinimal(value, flags) {
|
|
|
|
|
var pushdata = value.pushdata;
|
|
|
|
|
|
|
|
|
|
if (flags == null)
|
|
|
|
|
@ -2012,6 +2101,9 @@ Script.createCommitment = function createCommitment(hash) {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.getRedeem = function getRedeem() {
|
|
|
|
|
if (this.mutable)
|
|
|
|
|
return Script.getRedeem(this.code);
|
|
|
|
|
|
|
|
|
|
if (!this.redeem)
|
|
|
|
|
this.redeem = Script.getRedeem(this.code);
|
|
|
|
|
|
|
|
|
|
@ -2020,7 +2112,6 @@ Script.prototype.getRedeem = function getRedeem() {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Grab and deserialize the redeem script from script code.
|
|
|
|
|
* @static
|
|
|
|
|
* @param {Array} code
|
|
|
|
|
* @returns {Script|null} Redeem script.
|
|
|
|
|
*/
|
|
|
|
|
@ -2028,7 +2119,7 @@ Script.prototype.getRedeem = function getRedeem() {
|
|
|
|
|
Script.getRedeem = function getRedeem(code) {
|
|
|
|
|
var redeem = code[code.length - 1];
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(redeem))
|
|
|
|
|
if (!Script.isPush(redeem))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
return new Script(redeem);
|
|
|
|
|
@ -2086,9 +2177,6 @@ Script.prototype.isStandard = function isStandard() {
|
|
|
|
|
|
|
|
|
|
if (m < 1 || m > n)
|
|
|
|
|
return false;
|
|
|
|
|
} else if (type === 'nulldata') {
|
|
|
|
|
if (this.getSize() > constants.script.MAX_OP_RETURN_BYTES)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return type !== 'unknown';
|
|
|
|
|
@ -2136,10 +2224,14 @@ Script.prototype.isStandardProgram = function isStandardProgram(witness, flags)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @returns {Number} Size of script excluding the varint size bytes.
|
|
|
|
|
* Calculate size of script excluding the varint size bytes.
|
|
|
|
|
* @returns {Number}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.getSize = function getSize() {
|
|
|
|
|
if (this.mutable)
|
|
|
|
|
return Script.encode(this.code, new BufferWriter()).written;
|
|
|
|
|
|
|
|
|
|
return this.encode().length;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -2365,9 +2457,29 @@ Script.prototype.isScripthash = function isScripthash() {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.isNulldata = function isNulldata() {
|
|
|
|
|
return this.code.length === 2
|
|
|
|
|
&& this.code[0] === opcodes.OP_RETURN
|
|
|
|
|
&& Script.isData(this.code[1]);
|
|
|
|
|
var i, op;
|
|
|
|
|
|
|
|
|
|
if (this.raw && this.raw.length > constants.script.MAX_OP_RETURN_BYTES)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (this.code.length === 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (this.code[0] !== opcodes.OP_RETURN)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < this.code.length; i++) {
|
|
|
|
|
op = this.code[i];
|
|
|
|
|
if (Buffer.isBuffer(op)) {
|
|
|
|
|
if (!Script.checkPush(op))
|
|
|
|
|
return false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (op > opcodes.OP_16)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -2380,9 +2492,20 @@ Script.prototype.isNulldata = function isNulldata() {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.isCommitment = function isCommitment() {
|
|
|
|
|
if (this.raw) {
|
|
|
|
|
if (this.raw.length < 38)
|
|
|
|
|
return false;
|
|
|
|
|
if (this.raw[0] !== opcodes.OP_RETURN)
|
|
|
|
|
return false;
|
|
|
|
|
if (this.raw[1] !== 0x24)
|
|
|
|
|
return false;
|
|
|
|
|
if (utils.readU32BE(this.raw, 2) !== 0xaa21a9ed)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return this.code.length >= 2
|
|
|
|
|
&& this.code[0] === opcodes.OP_RETURN
|
|
|
|
|
&& Buffer.isBuffer(this.code[1])
|
|
|
|
|
&& Script.isPush(this.code[1])
|
|
|
|
|
&& this.code[1].length === 36
|
|
|
|
|
&& utils.readU32BE(this.code[1], 0) === 0xaa21a9ed;
|
|
|
|
|
};
|
|
|
|
|
@ -2407,13 +2530,29 @@ Script.prototype.getCommitmentHash = function getCommitmentHash() {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.isWitnessProgram = function isWitnessProgram() {
|
|
|
|
|
// Witness programs are strict minimaldata.
|
|
|
|
|
if (this.raw) {
|
|
|
|
|
if (!(this.raw.length >= 4 && this.raw.length <= 34))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (this.raw[0] !== opcodes.OP_0
|
|
|
|
|
&& !(this.raw[0] >= opcodes.OP_1 && this.raw[0] <= opcodes.OP_16)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.raw[1] + 2 !== this.raw.length)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.code.length !== 2)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (typeof this.code[0] !== 'number')
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(this.code[1]))
|
|
|
|
|
if (!Script.isPush(this.code[1]))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return (this.code[0] === opcodes.OP_0
|
|
|
|
|
@ -2531,7 +2670,6 @@ Script.isUnknownInput = function isUnknownInput(code, isWitness) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Automatically build an output script from any number of options.
|
|
|
|
|
* @static
|
|
|
|
|
* @example
|
|
|
|
|
* Script.createOutputScript({ address: '1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E' });
|
|
|
|
|
* @param {Object} options
|
|
|
|
|
@ -2712,7 +2850,7 @@ Script.isScripthashInput = function isScripthashInput(code, isWitness) {
|
|
|
|
|
|
|
|
|
|
// Last data element should be an array
|
|
|
|
|
// for the redeem script.
|
|
|
|
|
if (!Buffer.isBuffer(raw))
|
|
|
|
|
if (!Script.isPush(raw))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Testing for scripthash inputs requires
|
|
|
|
|
@ -2739,6 +2877,39 @@ Script.isScripthashInput = function isScripthashInput(code, isWitness) {
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get coinbase height.
|
|
|
|
|
* @returns {Number} `-1` if not present.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
|
|
|
|
return Script.getCoinbaseHeight(this.code);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get coinbase height.
|
|
|
|
|
* @returns {Number} `-1` if not present.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.getCoinbaseHeight = function getCoinbaseHeight(code) {
|
|
|
|
|
if (code.length === 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(code[0]))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (!Script.checkPush(code[0]))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (!Script.checkMinimal(code[0]))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (code[0].length > 6)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return new bn(code[0], 'le').toNumber();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get info about a coinbase script.
|
|
|
|
|
* @returns {Object} Object containing `height`,
|
|
|
|
|
@ -2749,10 +2920,7 @@ Script.prototype.getCoinbaseData = function getCoinbaseData() {
|
|
|
|
|
var coinbase = {};
|
|
|
|
|
var flags;
|
|
|
|
|
|
|
|
|
|
if (Buffer.isBuffer(this.code[0]) && this.code[0].length <= 6)
|
|
|
|
|
coinbase.height = new bn(this.code[0], 'le').toNumber();
|
|
|
|
|
else
|
|
|
|
|
coinbase.height = -1;
|
|
|
|
|
coinbase.height = this.getCoinbaseHeight();
|
|
|
|
|
|
|
|
|
|
if (Buffer.isBuffer(this.code[1]))
|
|
|
|
|
coinbase.extraNonce = new bn(this.code[1], 'le');
|
|
|
|
|
@ -2781,7 +2949,7 @@ Script.prototype.getCoinbaseData = function getCoinbaseData() {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isHash = function isHash(hash) {
|
|
|
|
|
return Buffer.isBuffer(hash) && hash.length === 20;
|
|
|
|
|
return Script.isPush(hash) && hash.length === 20;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -2792,7 +2960,7 @@ Script.isHash = function isHash(hash) {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isKey = function isKey(key) {
|
|
|
|
|
return Buffer.isBuffer(key) && key.length >= 33 && key.length <= 65;
|
|
|
|
|
return Script.isPush(key) && key.length >= 33 && key.length <= 65;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -2803,7 +2971,7 @@ Script.isKey = function isKey(key) {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isSignature = function isSignature(sig) {
|
|
|
|
|
return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73;
|
|
|
|
|
return Script.isPush(sig) && sig.length >= 9 && sig.length <= 73;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -2813,7 +2981,7 @@ Script.isSignature = function isSignature(sig) {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isDummy = function isDummy(data) {
|
|
|
|
|
return Buffer.isBuffer(data) && data.length === 0;
|
|
|
|
|
return Script.isPush(data) && data.length === 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -2830,16 +2998,6 @@ Script.isZero = function isZero(op) {
|
|
|
|
|
return Script.isDummy(op);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test whether the data element is a valid nulldata push.
|
|
|
|
|
* @param {Buffer?} data
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isData = function isData(data) {
|
|
|
|
|
return Buffer.isBuffer(data) && data.length <= constants.script.MAX_OP_RETURN;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test whether the data element is a valid key if VERIFY_STRICTENC is enabled.
|
|
|
|
|
* @param {Buffer} key
|
|
|
|
|
@ -2851,7 +3009,7 @@ Script.isValidKey = function isValidKey(key, flags) {
|
|
|
|
|
if (flags == null)
|
|
|
|
|
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(key))
|
|
|
|
|
if (!Script.isPush(key))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (flags & constants.flags.VERIFY_STRICTENC) {
|
|
|
|
|
@ -2871,7 +3029,7 @@ Script.isValidKey = function isValidKey(key, flags) {
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.isKeyEncoding = function isKeyEncoding(key) {
|
|
|
|
|
if (!Buffer.isBuffer(key))
|
|
|
|
|
if (!Script.isPush(key))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (key.length < 33)
|
|
|
|
|
@ -2905,7 +3063,7 @@ Script.isValidSignature = function isValidSignature(sig, flags) {
|
|
|
|
|
if (flags == null)
|
|
|
|
|
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(sig))
|
|
|
|
|
if (!Script.isPush(sig))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Allow empty sigs
|
|
|
|
|
@ -2948,7 +3106,7 @@ Script.isValidSignature = function isValidSignature(sig, flags) {
|
|
|
|
|
Script.isSignatureEncoding = function isSignatureEncoding(sig) {
|
|
|
|
|
var lenR, lenS;
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(sig))
|
|
|
|
|
if (!Script.isPush(sig))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
|
|
|
|
|
@ -3039,7 +3197,7 @@ Script.isSignatureEncoding = function isSignatureEncoding(sig) {
|
|
|
|
|
Script.isHashType = function isHashType(sig) {
|
|
|
|
|
var type;
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(sig))
|
|
|
|
|
if (!Script.isPush(sig))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (sig.length === 0)
|
|
|
|
|
@ -3061,7 +3219,7 @@ Script.isHashType = function isHashType(sig) {
|
|
|
|
|
|
|
|
|
|
Script.isLowDER = function isLowDER(sig) {
|
|
|
|
|
if (!sig.s) {
|
|
|
|
|
if (!Buffer.isBuffer(sig))
|
|
|
|
|
if (!Script.isPush(sig))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!Script.isSignatureEncoding(sig))
|
|
|
|
|
@ -3130,13 +3288,13 @@ Script.prototype.isPushOnly = function isPushOnly() {
|
|
|
|
|
var i, op;
|
|
|
|
|
for (i = 0; i < this.code.length; i++) {
|
|
|
|
|
op = this.code[i];
|
|
|
|
|
if (Buffer.isBuffer(op)
|
|
|
|
|
|| op === opcodes.OP_1NEGATE
|
|
|
|
|
|| op === opcodes.OP_0
|
|
|
|
|
|| (op >= opcodes.OP_1 && op <= opcodes.OP_16)) {
|
|
|
|
|
if (Buffer.isBuffer(op)) {
|
|
|
|
|
if (!Script.checkPush(op))
|
|
|
|
|
return false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
if (op > opcodes.OP_16)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
@ -3156,7 +3314,7 @@ Script.prototype.getSigops = function getSigops(accurate) {
|
|
|
|
|
for (i = 0; i < this.code.length; i++) {
|
|
|
|
|
op = this.code[i];
|
|
|
|
|
|
|
|
|
|
if (Buffer.isBuffer(op))
|
|
|
|
|
if (Script.isPush(op))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (constants.opcodesByVal[op] == null)
|
|
|
|
|
@ -3407,7 +3565,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
|
|
|
|
|
// Grab the real redeem script
|
|
|
|
|
raw = stack.pop();
|
|
|
|
|
|
|
|
|
|
if (!Buffer.isBuffer(raw))
|
|
|
|
|
if (!Script.isPush(raw))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
redeem = new Script(raw);
|
|
|
|
|
@ -3628,6 +3786,20 @@ Script.parseScript = function parseScript(buf) {
|
|
|
|
|
* this will apply hidden `pushdata` properties
|
|
|
|
|
* to each Buffer if the buffer was created from
|
|
|
|
|
* a non-standard pushdata.
|
|
|
|
|
*
|
|
|
|
|
* This function does not do bounds checking
|
|
|
|
|
* on buffers because some jackass could do a
|
|
|
|
|
* direct push of 30 bytes with only 20 bytes
|
|
|
|
|
* after it. That script would be perfectly
|
|
|
|
|
* fine _until_ it is executed. There are
|
|
|
|
|
* output scripts on the blockchain that can
|
|
|
|
|
* never be redeemed due to this, but they are
|
|
|
|
|
* in valid blocks, therefore we cannot fail
|
|
|
|
|
* parsing them.
|
|
|
|
|
*
|
|
|
|
|
* Also note that this function uses reference
|
|
|
|
|
* Buffer slices. Larger buffer slices should
|
|
|
|
|
* _never_ be passed in here.
|
|
|
|
|
* @param {Buffer} buf - Serialized script.
|
|
|
|
|
* @returns {Array} Script code.
|
|
|
|
|
*/
|
|
|
|
|
@ -3639,12 +3811,6 @@ Script.decode = function decode(buf) {
|
|
|
|
|
|
|
|
|
|
assert(Buffer.isBuffer(buf));
|
|
|
|
|
|
|
|
|
|
// NOTE: We can't use a BufferReader here since
|
|
|
|
|
// script parsing was originally non-strict/ridiculous.
|
|
|
|
|
// Something could do a direct push of 30 bytes with
|
|
|
|
|
// only 20 bytes after it.
|
|
|
|
|
// NOTE 2: We use reference Buffer slices here. Larger
|
|
|
|
|
// buffer slices should _never_ be passed in here.
|
|
|
|
|
while (off < buf.length) {
|
|
|
|
|
op = buf[off++];
|
|
|
|
|
if (op >= 0x01 && op <= 0x4b) {
|
|
|
|
|
@ -3708,8 +3874,8 @@ Script.decode = function decode(buf) {
|
|
|
|
|
* @returns {Buffer} Serialized script.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Script.encode = function encode(code) {
|
|
|
|
|
var p = new BufferWriter();
|
|
|
|
|
Script.encode = function encode(code, writer) {
|
|
|
|
|
var p = new BufferWriter(writer);
|
|
|
|
|
var i = 0;
|
|
|
|
|
var op;
|
|
|
|
|
|
|
|
|
|
@ -3772,7 +3938,10 @@ Script.encode = function encode(code) {
|
|
|
|
|
p.writeU8(op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.render();
|
|
|
|
|
if (!writer)
|
|
|
|
|
p = p.render();
|
|
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|