script: move static methods off of script constructor.
This commit is contained in:
parent
49976e7d6f
commit
8893131e08
@ -1300,6 +1300,9 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) {
|
||||
if (flags & Script.flags.VERIFY_P2SH)
|
||||
cost += this.getScripthashSigops(view) * scale;
|
||||
|
||||
if ((flags & Script.flags.VERIFY_WITNESS) === 0)
|
||||
return cost;
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
input = this.inputs[i];
|
||||
coin = view.getOutput(input);
|
||||
@ -1307,11 +1310,7 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) {
|
||||
if (!coin)
|
||||
continue;
|
||||
|
||||
cost += Script.getWitnessSigops(
|
||||
input.script,
|
||||
coin.script,
|
||||
input.witness,
|
||||
flags);
|
||||
cost += coin.script.getWitnessSigops(input.script, input.witness);
|
||||
}
|
||||
|
||||
return cost;
|
||||
@ -1326,10 +1325,6 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) {
|
||||
|
||||
TX.prototype.getSigops = function getSigops(view, flags) {
|
||||
var scale = consensus.WITNESS_SCALE_FACTOR;
|
||||
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
return (this.getSigopsCost(view, flags) + scale - 1) / scale | 0;
|
||||
};
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
var assert = require('assert');
|
||||
var BN = require('bn.js');
|
||||
var util = require('../utils/util');
|
||||
var ec = require('../crypto/ec');
|
||||
|
||||
/**
|
||||
* Script opcodes.
|
||||
@ -298,7 +299,7 @@ exports.typesByVal = util.revMap(exports.types);
|
||||
* @const {Buffer}
|
||||
*/
|
||||
|
||||
exports.STACK_FALSE = Buffer.alloc(0);
|
||||
exports.STACK_FALSE = Buffer.from([]);
|
||||
|
||||
/**
|
||||
* True stack return value.
|
||||
@ -314,6 +315,59 @@ exports.STACK_TRUE = Buffer.from([0x01]);
|
||||
|
||||
exports.STACK_NEGATE = Buffer.from([0x81]);
|
||||
|
||||
/**
|
||||
* Test a signature to see whether it contains a valid sighash type.
|
||||
* @param {Buffer} sig
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isHashType = function isHashType(sig) {
|
||||
var type;
|
||||
|
||||
assert(Buffer.isBuffer(sig));
|
||||
|
||||
if (sig.length === 0)
|
||||
return false;
|
||||
|
||||
type = sig[sig.length - 1] & ~exports.hashType.ANYONECANPAY;
|
||||
|
||||
if (!(type >= exports.hashType.ALL && type <= exports.hashType.SINGLE))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a signature to see whether it contains a low S value.
|
||||
* @param {Buffer} sig
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isLowDER = function isLowDER(sig) {
|
||||
if (!exports.isSignatureEncoding(sig))
|
||||
return false;
|
||||
|
||||
return ec.isLowS(sig.slice(0, -1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a small integer from an opcode (OP_0-OP_16).
|
||||
* @param {Number} index
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
exports.getSmall = function getSmall(op) {
|
||||
assert(typeof op === 'number');
|
||||
|
||||
if (op === exports.opcodes.OP_0)
|
||||
return 0;
|
||||
|
||||
if (op >= exports.opcodes.OP_1 && op <= exports.opcodes.OP_16)
|
||||
return op - 0x50;
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a ripemd160 hash.
|
||||
* @param {Buffer?} hash
|
||||
@ -346,34 +400,6 @@ exports.isSignature = function isSignature(sig) {
|
||||
return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a null dummy (a zero-length array).
|
||||
* @param {Buffer?} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isDummy = function isDummy(data) {
|
||||
return Buffer.isBuffer(data) && data.length === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a compressed key.
|
||||
* @param {Buffer} key
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isCompressedEncoding = function isCompressedEncoding(key) {
|
||||
assert(Buffer.isBuffer(key));
|
||||
|
||||
if (key.length !== 33)
|
||||
return false;
|
||||
|
||||
if (key[0] !== 0x02 && key[0] !== 0x03)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid key.
|
||||
* @param {Buffer} key
|
||||
@ -399,6 +425,24 @@ exports.isKeyEncoding = function isKeyEncoding(key) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a compressed key.
|
||||
* @param {Buffer} key
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.isCompressedEncoding = function isCompressedEncoding(key) {
|
||||
assert(Buffer.isBuffer(key));
|
||||
|
||||
if (key.length !== 33)
|
||||
return false;
|
||||
|
||||
if (key[0] !== 0x02 && key[0] !== 0x03)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a signature to see if it abides by BIP66.
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
|
||||
@ -34,6 +34,36 @@ function Opcode(value, data) {
|
||||
this.data = data || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a pushdata abides by minimaldata.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Opcode.prototype.isMinimal = function isMinimal() {
|
||||
if (!this.data)
|
||||
return true;
|
||||
|
||||
if (this.data.length === 0)
|
||||
return this.value === opcodes.OP_0;
|
||||
|
||||
if (this.data.length === 1 && this.data[0] >= 1 && this.data[0] <= 16)
|
||||
return false;
|
||||
|
||||
if (this.data.length === 1 && this.data[0] === 0x81)
|
||||
return false;
|
||||
|
||||
if (this.data.length <= 75)
|
||||
return this.value === this.data.length;
|
||||
|
||||
if (this.data.length <= 255)
|
||||
return this.value === opcodes.OP_PUSHDATA1;
|
||||
|
||||
if (this.data.length <= 65535)
|
||||
return this.value === opcodes.OP_PUSHDATA2;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode the opcode to a buffer writer.
|
||||
* @param {BufferWriter} bw
|
||||
|
||||
@ -507,6 +507,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
||||
var alt = [];
|
||||
var state = [];
|
||||
var negate = 0;
|
||||
var minimal = false;
|
||||
var op, code, data;
|
||||
var val, v1, v2, v3;
|
||||
var num, n1, n2, n3;
|
||||
@ -518,6 +519,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (version == null)
|
||||
version = 0;
|
||||
|
||||
if (flags & common.flags.VERIFY_MINIMALDATA)
|
||||
minimal = true;
|
||||
|
||||
if (this.getSize() > consensus.MAX_SCRIPT_SIZE)
|
||||
throw new ScriptError('SCRIPT_SIZE');
|
||||
|
||||
@ -536,7 +543,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
||||
// Note that minimaldata is not checked
|
||||
// on unexecuted branches of code.
|
||||
if (negate === 0) {
|
||||
if (!Script.isMinimal(data, op, flags))
|
||||
if (minimal && !code.isMinimal())
|
||||
throw new ScriptError('MINIMALDATA', op, ip);
|
||||
stack.push(data);
|
||||
}
|
||||
@ -1180,8 +1187,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
||||
if (version === 0)
|
||||
subscript.removeData(sig);
|
||||
|
||||
Script.validateSignature(sig, flags);
|
||||
Script.validateKey(key, flags, version);
|
||||
validateSignature(sig, flags);
|
||||
validateKey(key, flags, version);
|
||||
|
||||
if (sig.length > 0) {
|
||||
type = sig[sig.length - 1];
|
||||
@ -1259,8 +1266,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
||||
sig = stack.top(-isig);
|
||||
key = stack.top(-ikey);
|
||||
|
||||
Script.validateSignature(sig, flags);
|
||||
Script.validateKey(key, flags, version);
|
||||
validateSignature(sig, flags);
|
||||
validateKey(key, flags, version);
|
||||
|
||||
if (sig.length > 0) {
|
||||
type = sig[sig.length - 1];
|
||||
@ -1293,7 +1300,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
|
||||
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
|
||||
|
||||
if (flags & Script.flags.VERIFY_NULLDUMMY) {
|
||||
if (!Script.isDummy(stack.top(-1)))
|
||||
if (stack.top(-1).length !== 0)
|
||||
throw new ScriptError('SIG_NULLDUMMY', op, ip);
|
||||
}
|
||||
|
||||
@ -1526,7 +1533,7 @@ Script.prototype.removeData = function removeData(data) {
|
||||
if (!op.data)
|
||||
continue;
|
||||
|
||||
if (!Script.isMinimal(op.data, op.value))
|
||||
if (!op.isMinimal())
|
||||
continue;
|
||||
|
||||
if (util.equal(op.data, data))
|
||||
@ -1571,57 +1578,16 @@ Script.prototype.indexOf = function indexOf(data) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Check to see if a pushdata Buffer abides by minimaldata.
|
||||
* @param {Buffer} data
|
||||
* @param {Number} opcode
|
||||
* @param {Number?} flags
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isMinimal = function isMinimal(data, opcode, flags) {
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (!(flags & Script.flags.VERIFY_MINIMALDATA))
|
||||
return true;
|
||||
|
||||
if (!data)
|
||||
return true;
|
||||
|
||||
if (data.length === 0)
|
||||
return opcode === opcodes.OP_0;
|
||||
|
||||
if (data.length === 1 && data[0] >= 1 && data[0] <= 16)
|
||||
return false;
|
||||
|
||||
if (data.length === 1 && data[0] === 0x81)
|
||||
return false;
|
||||
|
||||
if (data.length <= 75)
|
||||
return opcode === data.length;
|
||||
|
||||
if (data.length <= 255)
|
||||
return opcode === opcodes.OP_PUSHDATA1;
|
||||
|
||||
if (data.length <= 65535)
|
||||
return opcode === opcodes.OP_PUSHDATA2;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a buffer to see if it is valid
|
||||
* Test a script to see if it is valid
|
||||
* script code (no non-existent opcodes).
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isCode = function isCode(raw) {
|
||||
var script = Script.fromRaw(raw);
|
||||
Script.prototype.isCode = function isCode() {
|
||||
var i, op;
|
||||
|
||||
for (i = 0; i < script.code.length; i++) {
|
||||
op = script.code[i];
|
||||
for (i = 0; i < this.code.length; i++) {
|
||||
op = this.code[i];
|
||||
|
||||
if (op.data)
|
||||
continue;
|
||||
@ -2093,7 +2059,7 @@ Script.prototype.isPubkey = function isPubkey(minimal) {
|
||||
}
|
||||
|
||||
return this.code.length === 2
|
||||
&& Script.isKey(this.code[0].data)
|
||||
&& common.isKey(this.code[0].data)
|
||||
&& this.code[1].value === opcodes.OP_CHECKSIG;
|
||||
};
|
||||
|
||||
@ -2116,7 +2082,7 @@ Script.prototype.isPubkeyhash = function isPubkeyhash(minimal) {
|
||||
return this.code.length === 5
|
||||
&& this.code[0].value === opcodes.OP_DUP
|
||||
&& this.code[1].value === opcodes.OP_HASH160
|
||||
&& Script.isHash(this.code[2].data)
|
||||
&& common.isHash(this.code[2].data)
|
||||
&& this.code[3].value === opcodes.OP_EQUALVERIFY
|
||||
&& this.code[4].value === opcodes.OP_CHECKSIG;
|
||||
};
|
||||
@ -2136,12 +2102,12 @@ Script.prototype.isMultisig = function isMultisig(minimal) {
|
||||
if (this.raw[this.raw.length - 1] !== opcodes.OP_CHECKMULTISIG)
|
||||
return false;
|
||||
|
||||
n = Script.getSmall(this.raw[this.raw.length - 2]);
|
||||
n = common.getSmall(this.raw[this.raw.length - 2]);
|
||||
|
||||
if (n < 1)
|
||||
return false;
|
||||
|
||||
m = Script.getSmall(this.raw[0]);
|
||||
m = common.getSmall(this.raw[0]);
|
||||
|
||||
if (!(m >= 1 && m <= n))
|
||||
return false;
|
||||
@ -2152,11 +2118,11 @@ Script.prototype.isMultisig = function isMultisig(minimal) {
|
||||
for (i = 1; i < n + 1; i++) {
|
||||
op = this.code[i];
|
||||
|
||||
if (!Script.isKey(op.data))
|
||||
if (!common.isKey(op.data))
|
||||
return false;
|
||||
|
||||
if (minimal) {
|
||||
if (!Script.isMinimal(op.data, op.value))
|
||||
if (!op.isMinimal())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2202,7 +2168,7 @@ Script.prototype.isNulldata = function isNulldata(minimal) {
|
||||
return false;
|
||||
|
||||
if (this.raw.length === 2)
|
||||
return Script.getSmall(this.raw[1]) !== -1;
|
||||
return common.getSmall(this.raw[1]) !== -1;
|
||||
|
||||
if (this.raw[1] >= 0x01 && this.raw[1] <= 0x4b)
|
||||
return this.raw[1] + 2 === this.raw.length;
|
||||
@ -2287,7 +2253,7 @@ Script.prototype.toProgram = function toProgram() {
|
||||
if (!this.isProgram())
|
||||
return;
|
||||
|
||||
version = Script.getSmall(this.raw[0]);
|
||||
version = common.getSmall(this.raw[0]);
|
||||
data = this.raw.slice(2);
|
||||
|
||||
return new Program(version, data);
|
||||
@ -2412,7 +2378,7 @@ Script.prototype.isPubkeyInput = function isPubkeyInput() {
|
||||
if (this.raw[0] > opcodes.OP_PUSHDATA4)
|
||||
return false;
|
||||
|
||||
return this.code.length === 1 && Script.isSignature(this.code[0].data);
|
||||
return this.code.length === 1 && common.isSignature(this.code[0].data);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2432,8 +2398,8 @@ Script.prototype.isPubkeyhashInput = function isPubkeyhashInput() {
|
||||
return false;
|
||||
|
||||
return this.code.length === 2
|
||||
&& Script.isSignature(this.code[0].data)
|
||||
&& Script.isKey(this.code[1].data);
|
||||
&& common.isSignature(this.code[0].data)
|
||||
&& common.isKey(this.code[1].data);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2463,7 +2429,7 @@ Script.prototype.isMultisigInput = function isMultisigInput() {
|
||||
return false;
|
||||
|
||||
for (i = 1; i < this.code.length; i++) {
|
||||
if (!Script.isSignature(this.code[i].data))
|
||||
if (!common.isSignature(this.code[i].data))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2477,7 +2443,7 @@ Script.prototype.isMultisigInput = function isMultisigInput() {
|
||||
*/
|
||||
|
||||
Script.prototype.isScripthashInput = function isScripthashInput() {
|
||||
var op;
|
||||
var op, redeem;
|
||||
|
||||
if (this.raw.length < 2)
|
||||
return false;
|
||||
@ -2499,16 +2465,18 @@ Script.prototype.isScripthashInput = function isScripthashInput() {
|
||||
// key, and we ensure that it is at least
|
||||
// a script that does not use undefined
|
||||
// opcodes.
|
||||
if (Script.isDummy(op.data))
|
||||
if (op.data.length === 0)
|
||||
return false;
|
||||
|
||||
if (Script.isSignatureEncoding(op.data))
|
||||
if (common.isSignatureEncoding(op.data))
|
||||
return false;
|
||||
|
||||
if (Script.isKeyEncoding(op.data))
|
||||
if (common.isKeyEncoding(op.data))
|
||||
return false;
|
||||
|
||||
if (!Script.isCode(op.data))
|
||||
redeem = Script.fromRaw(op.data);
|
||||
|
||||
if (!redeem.isCode())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -2537,7 +2505,7 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) {
|
||||
return -1;
|
||||
|
||||
// Small ints are allowed.
|
||||
height = Script.getSmall(raw[0]);
|
||||
height = common.getSmall(raw[0]);
|
||||
|
||||
if (height !== -1)
|
||||
return height;
|
||||
@ -2623,25 +2591,29 @@ Script.prototype.push = function push(data) {
|
||||
|
||||
/**
|
||||
* Shift an item off of the `code` array.
|
||||
* @returns {Buffer|Number}
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Script.prototype.shift = function shift() {
|
||||
var op = this.code.shift();
|
||||
|
||||
if (!op)
|
||||
return;
|
||||
return null;
|
||||
|
||||
return op.data || op.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pop an item off of the `code` array.
|
||||
* @returns {Buffer|Number}
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Script.prototype.pop = function push(data) {
|
||||
var op = this.code.pop();
|
||||
|
||||
if (!op)
|
||||
return;
|
||||
return null;
|
||||
|
||||
return op.data || op.value;
|
||||
};
|
||||
|
||||
@ -2653,8 +2625,10 @@ Script.prototype.pop = function push(data) {
|
||||
|
||||
Script.prototype.remove = function remove(i) {
|
||||
var op = this.code.splice(i, 1)[0];
|
||||
|
||||
if (!op)
|
||||
return;
|
||||
return null;
|
||||
|
||||
return op.data || op.value;
|
||||
};
|
||||
|
||||
@ -2666,19 +2640,21 @@ Script.prototype.remove = function remove(i) {
|
||||
|
||||
Script.prototype.insert = function insert(i, data) {
|
||||
assert(i <= this.code.length, 'Index out of bounds.');
|
||||
this.code.splice(i, 0, Opcode.from(data))[0];
|
||||
this.code.splice(i, 0, Opcode.from(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an item from the `code` array.
|
||||
* @param {Number} index
|
||||
* @returns {Buffer|Number}
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Script.prototype.get = function get(i) {
|
||||
var op = this.code[i];
|
||||
|
||||
if (!op)
|
||||
return;
|
||||
return null;
|
||||
|
||||
return op.data || op.value;
|
||||
};
|
||||
|
||||
@ -2690,9 +2666,11 @@ Script.prototype.get = function get(i) {
|
||||
|
||||
Script.prototype.getSmall = function getSmall(i) {
|
||||
var op = this.code[i];
|
||||
|
||||
if (!op)
|
||||
return -1;
|
||||
return Script.getSmall(op.value);
|
||||
|
||||
return common.getSmall(op.value);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2709,7 +2687,7 @@ Script.prototype.getNumber = function getNumber(i) {
|
||||
return new BN(small);
|
||||
|
||||
if (!op || !op.data || op.data.length > 5)
|
||||
return;
|
||||
return null;
|
||||
|
||||
return Script.num(op.data, Script.flags.VERIFY_NONE, 5);
|
||||
};
|
||||
@ -2722,8 +2700,10 @@ Script.prototype.getNumber = function getNumber(i) {
|
||||
|
||||
Script.prototype.getString = function getString(i) {
|
||||
var op = this.code[i];
|
||||
|
||||
if (!op || !op.data)
|
||||
return;
|
||||
return null;
|
||||
|
||||
return op.data.toString('utf8');
|
||||
};
|
||||
|
||||
@ -2746,16 +2726,6 @@ Script.prototype.set = function set(i, data) {
|
||||
this.code[i] = Opcode.from(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a ripemd160 hash.
|
||||
* @param {Buffer?} hash
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isHash = function isHash(hash) {
|
||||
return common.isHash(hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a public key. Note that
|
||||
* this does not verify the format of the key, only the length.
|
||||
@ -2778,153 +2748,6 @@ Script.isSignature = function isSignature(sig) {
|
||||
return common.isSignature(sig);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a null dummy (a zero-length array).
|
||||
* @param {Buffer?} data
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isDummy = function isDummy(data) {
|
||||
return common.isDummy(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid key if VERIFY_STRICTENC is enabled.
|
||||
* @param {Buffer} key
|
||||
* @param {VerifyFlags?} flags
|
||||
* @returns {Boolean}
|
||||
* @throws {ScriptError}
|
||||
*/
|
||||
|
||||
Script.validateKey = function validateKey(key, flags, version) {
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
assert(Buffer.isBuffer(key));
|
||||
|
||||
if (flags & Script.flags.VERIFY_STRICTENC) {
|
||||
if (!Script.isKeyEncoding(key))
|
||||
throw new ScriptError('PUBKEYTYPE');
|
||||
}
|
||||
|
||||
if (version === 1) {
|
||||
if (flags & Script.flags.VERIFY_WITNESS_PUBKEYTYPE) {
|
||||
if (!Script.isCompressedEncoding(key))
|
||||
throw new ScriptError('WITNESS_PUBKEYTYPE');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a compressed key.
|
||||
* @param {Buffer} key
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isCompressedEncoding = function isCompressedEncoding(key) {
|
||||
return common.isCompressedEncoding(key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid key.
|
||||
* @param {Buffer} key
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isKeyEncoding = function isKeyEncoding(key) {
|
||||
return common.isKeyEncoding(key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid signature based
|
||||
* on the encoding, S value, and sighash type. Requires
|
||||
* VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, VERIFY_LOW_S
|
||||
* and VERIFY_STRING_ENC to be enabled respectively. Note that
|
||||
* this will allow zero-length signatures.
|
||||
* @param {Buffer} sig
|
||||
* @param {VerifyFlags?} flags
|
||||
* @returns {Boolean}
|
||||
* @throws {ScriptError}
|
||||
*/
|
||||
|
||||
Script.validateSignature = function validateSignature(sig, flags) {
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
assert(Buffer.isBuffer(sig));
|
||||
|
||||
// Allow empty sigs
|
||||
if (sig.length === 0)
|
||||
return true;
|
||||
|
||||
if ((flags & Script.flags.VERIFY_DERSIG)
|
||||
|| (flags & Script.flags.VERIFY_LOW_S)
|
||||
|| (flags & Script.flags.VERIFY_STRICTENC)) {
|
||||
if (!Script.isSignatureEncoding(sig))
|
||||
throw new ScriptError('SIG_DER');
|
||||
}
|
||||
|
||||
if (flags & Script.flags.VERIFY_LOW_S) {
|
||||
if (!Script.isLowDER(sig))
|
||||
throw new ScriptError('SIG_HIGH_S');
|
||||
}
|
||||
|
||||
if (flags & Script.flags.VERIFY_STRICTENC) {
|
||||
if (!Script.isHashType(sig))
|
||||
throw new ScriptError('SIG_HASHTYPE');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a signature to see if it abides by BIP66.
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
* @param {Buffer} sig
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isSignatureEncoding = function isSignatureEncoding(sig) {
|
||||
return common.isSignatureEncoding(sig);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a signature to see whether it contains a valid sighash type.
|
||||
* @param {Buffer} sig
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isHashType = function isHashType(sig) {
|
||||
var type;
|
||||
|
||||
assert(Buffer.isBuffer(sig));
|
||||
|
||||
if (sig.length === 0)
|
||||
return false;
|
||||
|
||||
type = sig[sig.length - 1] & ~Script.hashType.ANYONECANPAY;
|
||||
|
||||
if (!(type >= Script.hashType.ALL && type <= Script.hashType.SINGLE))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test a signature to see whether it contains a low S value.
|
||||
* @param {Buffer} sig
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Script.isLowDER = function isLowDER(sig) {
|
||||
if (!Script.isSignatureEncoding(sig))
|
||||
return false;
|
||||
|
||||
return ec.isLowS(sig.slice(0, -1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Test the script to see if it contains only push ops.
|
||||
* Push ops are: OP_1NEGATE, OP_0-OP_16 and all PUSHDATAs.
|
||||
@ -3031,16 +2854,12 @@ Script.prototype.getScripthashSigops = function getScripthashSigops(input) {
|
||||
* Count the sigops for a program.
|
||||
* @param {Program} program
|
||||
* @param {Witness} witness
|
||||
* @param {VerifyFlags} flags
|
||||
* @returns {Number} sigop count
|
||||
*/
|
||||
|
||||
Script.witnessSigops = function witnessSigops(program, witness, flags) {
|
||||
Script.witnessSigops = function witnessSigops(program, witness) {
|
||||
var redeem;
|
||||
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (program.version === 0) {
|
||||
if (program.data.length === 20)
|
||||
return 1;
|
||||
@ -3057,25 +2876,15 @@ Script.witnessSigops = function witnessSigops(program, witness, flags) {
|
||||
/**
|
||||
* Count the sigops in a script, taking into account witness programs.
|
||||
* @param {Script} input
|
||||
* @param {Script} output
|
||||
* @param {Witness} witness
|
||||
* @param {VerifyFlags} flags
|
||||
* @returns {Number} sigop count
|
||||
*/
|
||||
|
||||
Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flags) {
|
||||
Script.prototype.getWitnessSigops = function getWitnessSigops(input, witness) {
|
||||
var redeem;
|
||||
|
||||
if (flags == null)
|
||||
flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if ((flags & Script.flags.VERIFY_WITNESS) === 0)
|
||||
return 0;
|
||||
|
||||
assert((flags & Script.flags.VERIFY_P2SH) !== 0);
|
||||
|
||||
if (output.isProgram())
|
||||
return Script.witnessSigops(output.toProgram(), witness, flags);
|
||||
if (this.isProgram())
|
||||
return Script.witnessSigops(this.toProgram(), witness);
|
||||
|
||||
// This is a unique situation in terms of consensus
|
||||
// rules. We can just grab the redeem script without
|
||||
@ -3084,10 +2893,10 @@ Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flag
|
||||
// which checks for parse errors and will return
|
||||
// false if one is found. Even the bitcoind code
|
||||
// does not check the return value of GetOp.
|
||||
if (output.isScripthash() && input.isPushOnly()) {
|
||||
if (this.isScripthash() && input.isPushOnly()) {
|
||||
redeem = input.getRedeem();
|
||||
if (redeem && redeem.isProgram())
|
||||
return Script.witnessSigops(redeem.toProgram(), witness, flags);
|
||||
return Script.witnessSigops(redeem.toProgram(), witness);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -3164,25 +2973,6 @@ Script.fromString = function fromString(code) {
|
||||
return new Script().fromString(code);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a small integer from an opcode (OP_0-OP_16).
|
||||
* @param {Number} index
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Script.getSmall = function getSmall(op) {
|
||||
if (typeof op !== 'number')
|
||||
return -1;
|
||||
|
||||
if (op === opcodes.OP_0)
|
||||
return 0;
|
||||
|
||||
if (op >= opcodes.OP_1 && op <= opcodes.OP_16)
|
||||
return op - 0x50;
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify an input and output script, and a witness if present.
|
||||
* @param {Script} input
|
||||
@ -3633,6 +3423,73 @@ function sortKeys(keys) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid key if VERIFY_STRICTENC is enabled.
|
||||
* @param {Buffer} key
|
||||
* @param {VerifyFlags?} flags
|
||||
* @returns {Boolean}
|
||||
* @throws {ScriptError}
|
||||
*/
|
||||
|
||||
function validateKey(key, flags, version) {
|
||||
assert(Buffer.isBuffer(key));
|
||||
assert(typeof flags === 'number');
|
||||
|
||||
if (flags & Script.flags.VERIFY_STRICTENC) {
|
||||
if (!common.isKeyEncoding(key))
|
||||
throw new ScriptError('PUBKEYTYPE');
|
||||
}
|
||||
|
||||
if (version === 1) {
|
||||
if (flags & Script.flags.VERIFY_WITNESS_PUBKEYTYPE) {
|
||||
if (!common.isCompressedEncoding(key))
|
||||
throw new ScriptError('WITNESS_PUBKEYTYPE');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the data element is a valid signature based
|
||||
* on the encoding, S value, and sighash type. Requires
|
||||
* VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, VERIFY_LOW_S
|
||||
* and VERIFY_STRING_ENC to be enabled respectively. Note that
|
||||
* this will allow zero-length signatures.
|
||||
* @param {Buffer} sig
|
||||
* @param {VerifyFlags?} flags
|
||||
* @returns {Boolean}
|
||||
* @throws {ScriptError}
|
||||
*/
|
||||
|
||||
function validateSignature(sig, flags) {
|
||||
assert(Buffer.isBuffer(sig));
|
||||
assert(typeof flags === 'number');
|
||||
|
||||
// Allow empty sigs
|
||||
if (sig.length === 0)
|
||||
return true;
|
||||
|
||||
if ((flags & Script.flags.VERIFY_DERSIG)
|
||||
|| (flags & Script.flags.VERIFY_LOW_S)
|
||||
|| (flags & Script.flags.VERIFY_STRICTENC)) {
|
||||
if (!common.isSignatureEncoding(sig))
|
||||
throw new ScriptError('SIG_DER');
|
||||
}
|
||||
|
||||
if (flags & Script.flags.VERIFY_LOW_S) {
|
||||
if (!common.isLowDER(sig))
|
||||
throw new ScriptError('SIG_HIGH_S');
|
||||
}
|
||||
|
||||
if (flags & Script.flags.VERIFY_STRICTENC) {
|
||||
if (!common.isHashType(sig))
|
||||
throw new ScriptError('SIG_HASHTYPE');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user