strict scripting.
This commit is contained in:
parent
ea7fb8f476
commit
a4f0807c50
@ -387,7 +387,7 @@ Block.prototype._verify = function _verify(ret) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||||
var coinbase, code, height;
|
var coinbase, height;
|
||||||
|
|
||||||
if (this.version < 2)
|
if (this.version < 2)
|
||||||
return -1;
|
return -1;
|
||||||
@ -400,13 +400,7 @@ Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
|||||||
if (!coinbase || coinbase.inputs.length === 0)
|
if (!coinbase || coinbase.inputs.length === 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
code = coinbase.inputs[0].script.code;
|
height = coinbase.inputs[0].script.getCoinbaseHeight();
|
||||||
|
|
||||||
if (Buffer.isBuffer(code[0]) && code[0].length <= 6)
|
|
||||||
height = new bn(code[0], 'le').toNumber();
|
|
||||||
else
|
|
||||||
height = -1;
|
|
||||||
|
|
||||||
this._cbHeight = height;
|
this._cbHeight = height;
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
|||||||
@ -55,12 +55,7 @@ function CompactBlock(data) {
|
|||||||
bcoin.abstractblock.call(this, data);
|
bcoin.abstractblock.call(this, data);
|
||||||
|
|
||||||
this.type = 'compactblock';
|
this.type = 'compactblock';
|
||||||
this.coinbaseHeight = -1;
|
this.coinbaseHeight = data.coinbaseHeight;
|
||||||
|
|
||||||
if (this.version >= 2) {
|
|
||||||
if (Buffer.isBuffer(data.coinbaseHeight) && data.coinbaseHeight.length <= 6)
|
|
||||||
this.coinbaseHeight = new bn(data.coinbaseHeight, 'le').toNumber();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.inherits(CompactBlock, bcoin.abstractblock);
|
utils.inherits(CompactBlock, bcoin.abstractblock);
|
||||||
|
|||||||
@ -18,7 +18,7 @@ var BufferWriter = require('./writer');
|
|||||||
* @exports Input
|
* @exports Input
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {NakedInput} options
|
* @param {NakedInput} options
|
||||||
* @param {TX?} tx
|
* @param {Boolean?} mutable
|
||||||
* @property {Object} prevout - Outpoint.
|
* @property {Object} prevout - Outpoint.
|
||||||
* @property {Hash} prevout.hash - Previous transaction hash.
|
* @property {Hash} prevout.hash - Previous transaction hash.
|
||||||
* @property {Number} prevout.index - Previous output index.
|
* @property {Number} prevout.index - Previous output index.
|
||||||
@ -28,17 +28,18 @@ var BufferWriter = require('./writer');
|
|||||||
* @property {Coin?} coin - Previous output.
|
* @property {Coin?} coin - Previous output.
|
||||||
* @property {String} type - Script type.
|
* @property {String} type - Script type.
|
||||||
* @property {String?} address - Input address.
|
* @property {String?} address - Input address.
|
||||||
|
* @property {Boolean} mutable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Input(options, tx) {
|
function Input(options, mutable) {
|
||||||
if (!(this instanceof Input))
|
if (!(this instanceof Input))
|
||||||
return new Input(options);
|
return new Input(options);
|
||||||
|
|
||||||
|
this.mutable = !!mutable;
|
||||||
this.prevout = options.prevout;
|
this.prevout = options.prevout;
|
||||||
this.script = bcoin.script(options.script);
|
this.script = bcoin.script(options.script, this.mutable);
|
||||||
this.sequence = options.sequence == null ? 0xffffffff : options.sequence;
|
this.sequence = options.sequence == null ? 0xffffffff : options.sequence;
|
||||||
this.witness = bcoin.script.witness(options.witness);
|
this.witness = bcoin.script.witness(options.witness, this.mutable);
|
||||||
this._mutable = !tx || (tx instanceof bcoin.mtx);
|
|
||||||
|
|
||||||
if (options.coin)
|
if (options.coin)
|
||||||
this.coin = bcoin.coin(options.coin);
|
this.coin = bcoin.coin(options.coin);
|
||||||
@ -83,7 +84,7 @@ Input.prototype.getType = function getType() {
|
|||||||
if (!type || type === 'unknown')
|
if (!type || type === 'unknown')
|
||||||
type = this.script.getInputType();
|
type = this.script.getInputType();
|
||||||
|
|
||||||
if (!this._mutable)
|
if (!this.mutable)
|
||||||
this._type = type;
|
this._type = type;
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
@ -160,7 +161,7 @@ Input.prototype.getAddress = function getAddress() {
|
|||||||
if (!address)
|
if (!address)
|
||||||
address = this.script.getInputAddress();
|
address = this.script.getInputAddress();
|
||||||
|
|
||||||
if (!this._mutable)
|
if (!this.mutable)
|
||||||
this._address = address;
|
this._address = address;
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
|
|||||||
@ -252,7 +252,7 @@ MTX.prototype.addInput = function addInput(options, index) {
|
|||||||
|
|
||||||
assert(options.prevout);
|
assert(options.prevout);
|
||||||
|
|
||||||
input = bcoin.input(options, this);
|
input = bcoin.input(options, true);
|
||||||
|
|
||||||
if (options.script instanceof Script)
|
if (options.script instanceof Script)
|
||||||
input.script = options.script.clone();
|
input.script = options.script.clone();
|
||||||
@ -386,7 +386,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
|
|||||||
vector[1] = addr.publicKey;
|
vector[1] = addr.publicKey;
|
||||||
} else if (prev.isMultisig()) {
|
} else if (prev.isMultisig()) {
|
||||||
// Multisig
|
// Multisig
|
||||||
if (utils.indexOf(prev.code, addr.publicKey) === -1)
|
if (prev.code.indexOf(addr.publicKey) === -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Already has a script template (at least)
|
// Already has a script template (at least)
|
||||||
@ -405,7 +405,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
|
|||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
vector[i + 1] = dummy;
|
vector[i + 1] = dummy;
|
||||||
} else {
|
} else {
|
||||||
if (utils.indexOf(prev.code, addr.publicKey) === -1)
|
if (prev.code.indexOf(addr.publicKey) === -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Already has a script template (at least)
|
// Already has a script template (at least)
|
||||||
@ -799,7 +799,7 @@ MTX.prototype.addOutput = function addOutput(obj, value) {
|
|||||||
options = obj;
|
options = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
output = bcoin.output(options, this);
|
output = bcoin.output(options, true);
|
||||||
|
|
||||||
this.outputs.push(output);
|
this.outputs.push(output);
|
||||||
|
|
||||||
|
|||||||
@ -16,14 +16,15 @@ var assert = utils.assert;
|
|||||||
* @exports Output
|
* @exports Output
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {NakedOutput} options
|
* @param {NakedOutput} options
|
||||||
* @param {TX?} tx
|
* @param {Boolean?} mutable
|
||||||
* @property {BN} value - Value in satoshis.
|
* @property {BN} value - Value in satoshis.
|
||||||
* @property {Script} script
|
* @property {Script} script
|
||||||
* @property {String} type - Script type.
|
* @property {String} type - Script type.
|
||||||
* @property {String?} address - Input address.
|
* @property {String?} address - Input address.
|
||||||
|
* @property {Boolean} mutable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Output(options, tx) {
|
function Output(options, mutable) {
|
||||||
var value;
|
var value;
|
||||||
|
|
||||||
if (!(this instanceof Output))
|
if (!(this instanceof Output))
|
||||||
@ -36,12 +37,12 @@ function Output(options, tx) {
|
|||||||
value = new bn(value);
|
value = new bn(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.mutable = !!mutable;
|
||||||
this.value = utils.satoshi(value || new bn(0));
|
this.value = utils.satoshi(value || new bn(0));
|
||||||
this.script = bcoin.script(options.script);
|
this.script = bcoin.script(options.script, this.mutable);
|
||||||
this._mutable = !tx || (tx instanceof bcoin.mtx);
|
|
||||||
|
|
||||||
assert(typeof value !== 'number');
|
assert(typeof value !== 'number');
|
||||||
assert(!this._mutable || !this.value.isNeg());
|
assert(!this.mutable || !this.value.isNeg());
|
||||||
}
|
}
|
||||||
|
|
||||||
Output.prototype.__defineGetter__('type', function() {
|
Output.prototype.__defineGetter__('type', function() {
|
||||||
@ -65,7 +66,7 @@ Output.prototype.getType = function getType() {
|
|||||||
|
|
||||||
type = this.script.getType();
|
type = this.script.getType();
|
||||||
|
|
||||||
if (!this._mutable)
|
if (!this.mutable)
|
||||||
this._type = type;
|
this._type = type;
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
@ -84,7 +85,7 @@ Output.prototype.getAddress = function getAddress() {
|
|||||||
|
|
||||||
address = this.script.getAddress();
|
address = this.script.getAddress();
|
||||||
|
|
||||||
if (!this._mutable)
|
if (!this.mutable)
|
||||||
this._address = address;
|
this._address = address;
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
|
|||||||
@ -937,10 +937,10 @@ Parser.parseBlockCompact = function parseBlockCompact(p) {
|
|||||||
input = Parser.parseInput(p);
|
input = Parser.parseInput(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input) {
|
if (input)
|
||||||
if (Buffer.isBuffer(input.script.code[0]))
|
height = bcoin.script.getCoinbaseHeight(input.script.code);
|
||||||
height = input.script.code[0];
|
else
|
||||||
}
|
height = -1;
|
||||||
|
|
||||||
raw = p.data;
|
raw = p.data;
|
||||||
|
|
||||||
|
|||||||
@ -35,15 +35,25 @@ var STACK_NEGATE = new Buffer([0xff]);
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @param {Buffer[]|Buffer|NakedWitness} items - Array of
|
* @param {Buffer[]|Buffer|NakedWitness} items - Array of
|
||||||
* stack items or raw witness buffer.
|
* 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) {
|
function Witness(items, mutable) {
|
||||||
if (items instanceof Witness)
|
if (items instanceof Witness) {
|
||||||
|
items.mutable = !!mutable;
|
||||||
|
items.redeem = null;
|
||||||
return items;
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(this instanceof Witness))
|
if (!(this instanceof Witness))
|
||||||
return new Witness(items);
|
return new Witness(items);
|
||||||
|
|
||||||
|
this.mutable = !!mutable;
|
||||||
|
|
||||||
if (!items)
|
if (!items)
|
||||||
items = [];
|
items = [];
|
||||||
|
|
||||||
@ -167,12 +177,25 @@ Witness.prototype.isUnknownInput = function isUnknownInput() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Witness.prototype.getRedeem = function getRedeem() {
|
Witness.prototype.getRedeem = function getRedeem() {
|
||||||
|
if (this.mutable)
|
||||||
|
return Script.getRedeem(this.items);
|
||||||
|
|
||||||
if (!this.redeem)
|
if (!this.redeem)
|
||||||
this.redeem = Script.getRedeem(this.items);
|
this.redeem = Script.getRedeem(this.items);
|
||||||
|
|
||||||
return this.redeem;
|
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
|
* Parse a formatted script
|
||||||
* string into a witness object. _Must_
|
* string into a witness object. _Must_
|
||||||
@ -761,18 +784,27 @@ Stack.isStack = function isStack(obj) {
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @param {Buffer|Array|Object|NakedScript} code - Array
|
* @param {Buffer|Array|Object|NakedScript} code - Array
|
||||||
* of script code or a serialized script Buffer.
|
* 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 {Array} code - Script code.
|
||||||
* @property {Buffer} raw - Serialized script.
|
* @property {Buffer?} raw - Serialized script.
|
||||||
* @property {Script} redeem - Redeem script.
|
* @property {Script?} redeem - Redeem script.
|
||||||
|
* @property {Boolean} mutable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Script(code) {
|
function Script(code, mutable) {
|
||||||
if (code instanceof Script)
|
if (code instanceof Script) {
|
||||||
|
code.mutable = !!mutable;
|
||||||
|
code.raw = null;
|
||||||
|
code.redeem = null;
|
||||||
return code;
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(this instanceof Script))
|
if (!(this instanceof Script))
|
||||||
return new Script(code);
|
return new Script(code);
|
||||||
|
|
||||||
|
this.mutable = !!mutable;
|
||||||
|
|
||||||
if (Buffer.isBuffer(code)) {
|
if (Buffer.isBuffer(code)) {
|
||||||
this.raw = code;
|
this.raw = code;
|
||||||
this.code = Script.decode(code);
|
this.code = Script.decode(code);
|
||||||
@ -791,6 +823,9 @@ function Script(code) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.mutable)
|
||||||
|
this.raw = null;
|
||||||
|
|
||||||
this.redeem = null;
|
this.redeem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,8 +855,12 @@ Script.prototype.inspect = function inspect() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.prototype.encode = function encode() {
|
Script.prototype.encode = function encode() {
|
||||||
|
if (this.mutable)
|
||||||
|
return Script.encode(this.code);
|
||||||
|
|
||||||
if (!this.raw)
|
if (!this.raw)
|
||||||
this.raw = Script.encode(this.code);
|
this.raw = Script.encode(this.code);
|
||||||
|
|
||||||
return this.raw;
|
return this.raw;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -920,12 +959,14 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
|||||||
op = this.code[ip];
|
op = this.code[ip];
|
||||||
|
|
||||||
if (Buffer.isBuffer(op)) {
|
if (Buffer.isBuffer(op)) {
|
||||||
|
if (!Script.checkPush(op))
|
||||||
|
throw new ScriptError('Pushdata out of range.', op, ip);
|
||||||
if (op.length > constants.script.MAX_PUSH)
|
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
|
// Note that minimaldata is not checked
|
||||||
// on unexecuted branches of code.
|
// on unexecuted branches of code.
|
||||||
if (stack.negate === 0) {
|
if (stack.negate === 0) {
|
||||||
if (!Script.checkPush(op, flags))
|
if (!Script.checkMinimal(op, flags))
|
||||||
throw new ScriptError('Push verification failed.', op, ip);
|
throw new ScriptError('Push verification failed.', op, ip);
|
||||||
stack.push(op);
|
stack.push(op);
|
||||||
}
|
}
|
||||||
@ -1366,7 +1407,8 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
|||||||
type = sig[sig.length - 1];
|
type = sig[sig.length - 1];
|
||||||
|
|
||||||
subscript = this.getSubscript(lastSep);
|
subscript = this.getSubscript(lastSep);
|
||||||
subscript.removeData(sig);
|
if (version === 0)
|
||||||
|
subscript.removeData(sig);
|
||||||
|
|
||||||
hash = tx.signatureHash(index, subscript, type, version);
|
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++) {
|
for (i = 0; i < m; i++) {
|
||||||
sig = stack.get(stack.length - 1 - i);
|
sig = stack.get(stack.length - 1 - i);
|
||||||
subscript.removeData(sig);
|
if (version === 0)
|
||||||
|
subscript.removeData(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
succ = 0;
|
succ = 0;
|
||||||
@ -1680,7 +1723,6 @@ Script.checkSequence = function checkSequence(sequence, tx, i) {
|
|||||||
/**
|
/**
|
||||||
* Cast a big number or Buffer to a bool.
|
* Cast a big number or Buffer to a bool.
|
||||||
* @see CastToBool
|
* @see CastToBool
|
||||||
* @static
|
|
||||||
* @param {BN|Buffer} value
|
* @param {BN|Buffer} value
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
@ -1707,7 +1749,6 @@ Script.bool = function bool(value) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a CScriptNum.
|
* Create a CScriptNum.
|
||||||
* @static
|
|
||||||
* @param {Buffer} value
|
* @param {Buffer} value
|
||||||
* @param {Number?} flags - Script standard flags.
|
* @param {Number?} flags - Script standard flags.
|
||||||
* @param {Number?} size - Max size in bytes.
|
* @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
|
* Create a script array. Will convert Numbers and big
|
||||||
* numbers to a little-endian buffer while taking into
|
* numbers to a little-endian buffer while taking into
|
||||||
* account negative zero, minimaldata, etc.
|
* account negative zero, minimaldata, etc.
|
||||||
* @static
|
|
||||||
* @example
|
* @example
|
||||||
* assert.deepEqual(Script.array(0), new Buffer([]));
|
* assert.deepEqual(Script.array(0), new Buffer([]));
|
||||||
* assert.deepEqual(Script.array(0xffee), new Buffer([0xee, 0xff]));
|
* 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
|
* a script's code (used to remove signatures
|
||||||
* before verification).
|
* before verification).
|
||||||
* @param {Buffer} data - Data element to match against.
|
* @param {Buffer} data - Data element to match against.
|
||||||
|
* @returns {Number} Total.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.prototype.removeData = function removeData(data) {
|
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]))
|
if (!Buffer.isBuffer(this.code[i]))
|
||||||
continue;
|
continue;
|
||||||
if (utils.isEqual(this.code[i], data))
|
if (utils.isEqual(this.code[i], data)) {
|
||||||
this.code.splice(i, 1);
|
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.
|
* Check to see if a pushdata Buffer abides by minimaldata.
|
||||||
* @static
|
|
||||||
* @param {Buffer} value - Pushdata op from script code
|
* @param {Buffer} value - Pushdata op from script code
|
||||||
* (must be from a deserialized script).
|
* (must be from a deserialized script).
|
||||||
* @param {Number?} flags
|
* @param {Number?} flags
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.checkPush = function checkPush(value, flags) {
|
Script.checkMinimal = function checkMinimal(value, flags) {
|
||||||
var pushdata = value.pushdata;
|
var pushdata = value.pushdata;
|
||||||
|
|
||||||
if (flags == null)
|
if (flags == null)
|
||||||
@ -2012,6 +2101,9 @@ Script.createCommitment = function createCommitment(hash) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.prototype.getRedeem = function getRedeem() {
|
Script.prototype.getRedeem = function getRedeem() {
|
||||||
|
if (this.mutable)
|
||||||
|
return Script.getRedeem(this.code);
|
||||||
|
|
||||||
if (!this.redeem)
|
if (!this.redeem)
|
||||||
this.redeem = Script.getRedeem(this.code);
|
this.redeem = Script.getRedeem(this.code);
|
||||||
|
|
||||||
@ -2020,7 +2112,6 @@ Script.prototype.getRedeem = function getRedeem() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Grab and deserialize the redeem script from script code.
|
* Grab and deserialize the redeem script from script code.
|
||||||
* @static
|
|
||||||
* @param {Array} code
|
* @param {Array} code
|
||||||
* @returns {Script|null} Redeem script.
|
* @returns {Script|null} Redeem script.
|
||||||
*/
|
*/
|
||||||
@ -2028,7 +2119,7 @@ Script.prototype.getRedeem = function getRedeem() {
|
|||||||
Script.getRedeem = function getRedeem(code) {
|
Script.getRedeem = function getRedeem(code) {
|
||||||
var redeem = code[code.length - 1];
|
var redeem = code[code.length - 1];
|
||||||
|
|
||||||
if (!Buffer.isBuffer(redeem))
|
if (!Script.isPush(redeem))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return new Script(redeem);
|
return new Script(redeem);
|
||||||
@ -2086,9 +2177,6 @@ Script.prototype.isStandard = function isStandard() {
|
|||||||
|
|
||||||
if (m < 1 || m > n)
|
if (m < 1 || m > n)
|
||||||
return false;
|
return false;
|
||||||
} else if (type === 'nulldata') {
|
|
||||||
if (this.getSize() > constants.script.MAX_OP_RETURN_BYTES)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return type !== 'unknown';
|
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() {
|
Script.prototype.getSize = function getSize() {
|
||||||
|
if (this.mutable)
|
||||||
|
return Script.encode(this.code, new BufferWriter()).written;
|
||||||
|
|
||||||
return this.encode().length;
|
return this.encode().length;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2365,9 +2457,29 @@ Script.prototype.isScripthash = function isScripthash() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.prototype.isNulldata = function isNulldata() {
|
Script.prototype.isNulldata = function isNulldata() {
|
||||||
return this.code.length === 2
|
var i, op;
|
||||||
&& this.code[0] === opcodes.OP_RETURN
|
|
||||||
&& Script.isData(this.code[1]);
|
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() {
|
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
|
return this.code.length >= 2
|
||||||
&& this.code[0] === opcodes.OP_RETURN
|
&& this.code[0] === opcodes.OP_RETURN
|
||||||
&& Buffer.isBuffer(this.code[1])
|
&& Script.isPush(this.code[1])
|
||||||
&& this.code[1].length === 36
|
&& this.code[1].length === 36
|
||||||
&& utils.readU32BE(this.code[1], 0) === 0xaa21a9ed;
|
&& utils.readU32BE(this.code[1], 0) === 0xaa21a9ed;
|
||||||
};
|
};
|
||||||
@ -2407,13 +2530,29 @@ Script.prototype.getCommitmentHash = function getCommitmentHash() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.prototype.isWitnessProgram = function isWitnessProgram() {
|
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)
|
if (this.code.length !== 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (typeof this.code[0] !== 'number')
|
if (typeof this.code[0] !== 'number')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Buffer.isBuffer(this.code[1]))
|
if (!Script.isPush(this.code[1]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (this.code[0] === opcodes.OP_0
|
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.
|
* Automatically build an output script from any number of options.
|
||||||
* @static
|
|
||||||
* @example
|
* @example
|
||||||
* Script.createOutputScript({ address: '1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E' });
|
* Script.createOutputScript({ address: '1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E' });
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
@ -2712,7 +2850,7 @@ Script.isScripthashInput = function isScripthashInput(code, isWitness) {
|
|||||||
|
|
||||||
// Last data element should be an array
|
// Last data element should be an array
|
||||||
// for the redeem script.
|
// for the redeem script.
|
||||||
if (!Buffer.isBuffer(raw))
|
if (!Script.isPush(raw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Testing for scripthash inputs requires
|
// Testing for scripthash inputs requires
|
||||||
@ -2739,6 +2877,39 @@ Script.isScripthashInput = function isScripthashInput(code, isWitness) {
|
|||||||
return true;
|
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.
|
* Get info about a coinbase script.
|
||||||
* @returns {Object} Object containing `height`,
|
* @returns {Object} Object containing `height`,
|
||||||
@ -2749,10 +2920,7 @@ Script.prototype.getCoinbaseData = function getCoinbaseData() {
|
|||||||
var coinbase = {};
|
var coinbase = {};
|
||||||
var flags;
|
var flags;
|
||||||
|
|
||||||
if (Buffer.isBuffer(this.code[0]) && this.code[0].length <= 6)
|
coinbase.height = this.getCoinbaseHeight();
|
||||||
coinbase.height = new bn(this.code[0], 'le').toNumber();
|
|
||||||
else
|
|
||||||
coinbase.height = -1;
|
|
||||||
|
|
||||||
if (Buffer.isBuffer(this.code[1]))
|
if (Buffer.isBuffer(this.code[1]))
|
||||||
coinbase.extraNonce = new bn(this.code[1], 'le');
|
coinbase.extraNonce = new bn(this.code[1], 'le');
|
||||||
@ -2781,7 +2949,7 @@ Script.prototype.getCoinbaseData = function getCoinbaseData() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.isHash = function isHash(hash) {
|
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) {
|
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) {
|
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) {
|
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);
|
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.
|
* Test whether the data element is a valid key if VERIFY_STRICTENC is enabled.
|
||||||
* @param {Buffer} key
|
* @param {Buffer} key
|
||||||
@ -2851,7 +3009,7 @@ Script.isValidKey = function isValidKey(key, flags) {
|
|||||||
if (flags == null)
|
if (flags == null)
|
||||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
|
|
||||||
if (!Buffer.isBuffer(key))
|
if (!Script.isPush(key))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (flags & constants.flags.VERIFY_STRICTENC) {
|
if (flags & constants.flags.VERIFY_STRICTENC) {
|
||||||
@ -2871,7 +3029,7 @@ Script.isValidKey = function isValidKey(key, flags) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Script.isKeyEncoding = function isKeyEncoding(key) {
|
Script.isKeyEncoding = function isKeyEncoding(key) {
|
||||||
if (!Buffer.isBuffer(key))
|
if (!Script.isPush(key))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (key.length < 33)
|
if (key.length < 33)
|
||||||
@ -2905,7 +3063,7 @@ Script.isValidSignature = function isValidSignature(sig, flags) {
|
|||||||
if (flags == null)
|
if (flags == null)
|
||||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
|
|
||||||
if (!Buffer.isBuffer(sig))
|
if (!Script.isPush(sig))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Allow empty sigs
|
// Allow empty sigs
|
||||||
@ -2948,7 +3106,7 @@ Script.isValidSignature = function isValidSignature(sig, flags) {
|
|||||||
Script.isSignatureEncoding = function isSignatureEncoding(sig) {
|
Script.isSignatureEncoding = function isSignatureEncoding(sig) {
|
||||||
var lenR, lenS;
|
var lenR, lenS;
|
||||||
|
|
||||||
if (!Buffer.isBuffer(sig))
|
if (!Script.isPush(sig))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
|
// 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) {
|
Script.isHashType = function isHashType(sig) {
|
||||||
var type;
|
var type;
|
||||||
|
|
||||||
if (!Buffer.isBuffer(sig))
|
if (!Script.isPush(sig))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (sig.length === 0)
|
if (sig.length === 0)
|
||||||
@ -3061,7 +3219,7 @@ Script.isHashType = function isHashType(sig) {
|
|||||||
|
|
||||||
Script.isLowDER = function isLowDER(sig) {
|
Script.isLowDER = function isLowDER(sig) {
|
||||||
if (!sig.s) {
|
if (!sig.s) {
|
||||||
if (!Buffer.isBuffer(sig))
|
if (!Script.isPush(sig))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Script.isSignatureEncoding(sig))
|
if (!Script.isSignatureEncoding(sig))
|
||||||
@ -3130,13 +3288,13 @@ Script.prototype.isPushOnly = function isPushOnly() {
|
|||||||
var i, op;
|
var i, op;
|
||||||
for (i = 0; i < this.code.length; i++) {
|
for (i = 0; i < this.code.length; i++) {
|
||||||
op = this.code[i];
|
op = this.code[i];
|
||||||
if (Buffer.isBuffer(op)
|
if (Buffer.isBuffer(op)) {
|
||||||
|| op === opcodes.OP_1NEGATE
|
if (!Script.checkPush(op))
|
||||||
|| op === opcodes.OP_0
|
return false;
|
||||||
|| (op >= opcodes.OP_1 && op <= opcodes.OP_16)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return false;
|
if (op > opcodes.OP_16)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -3156,7 +3314,7 @@ Script.prototype.getSigops = function getSigops(accurate) {
|
|||||||
for (i = 0; i < this.code.length; i++) {
|
for (i = 0; i < this.code.length; i++) {
|
||||||
op = this.code[i];
|
op = this.code[i];
|
||||||
|
|
||||||
if (Buffer.isBuffer(op))
|
if (Script.isPush(op))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (constants.opcodesByVal[op] == null)
|
if (constants.opcodesByVal[op] == null)
|
||||||
@ -3407,7 +3565,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
|
|||||||
// Grab the real redeem script
|
// Grab the real redeem script
|
||||||
raw = stack.pop();
|
raw = stack.pop();
|
||||||
|
|
||||||
if (!Buffer.isBuffer(raw))
|
if (!Script.isPush(raw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
redeem = new Script(raw);
|
redeem = new Script(raw);
|
||||||
@ -3628,6 +3786,20 @@ Script.parseScript = function parseScript(buf) {
|
|||||||
* this will apply hidden `pushdata` properties
|
* this will apply hidden `pushdata` properties
|
||||||
* to each Buffer if the buffer was created from
|
* to each Buffer if the buffer was created from
|
||||||
* a non-standard pushdata.
|
* 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.
|
* @param {Buffer} buf - Serialized script.
|
||||||
* @returns {Array} Script code.
|
* @returns {Array} Script code.
|
||||||
*/
|
*/
|
||||||
@ -3639,12 +3811,6 @@ Script.decode = function decode(buf) {
|
|||||||
|
|
||||||
assert(Buffer.isBuffer(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) {
|
while (off < buf.length) {
|
||||||
op = buf[off++];
|
op = buf[off++];
|
||||||
if (op >= 0x01 && op <= 0x4b) {
|
if (op >= 0x01 && op <= 0x4b) {
|
||||||
@ -3708,8 +3874,8 @@ Script.decode = function decode(buf) {
|
|||||||
* @returns {Buffer} Serialized script.
|
* @returns {Buffer} Serialized script.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Script.encode = function encode(code) {
|
Script.encode = function encode(code, writer) {
|
||||||
var p = new BufferWriter();
|
var p = new BufferWriter(writer);
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var op;
|
var op;
|
||||||
|
|
||||||
@ -3772,7 +3938,10 @@ Script.encode = function encode(code) {
|
|||||||
p.writeU8(op);
|
p.writeU8(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.render();
|
if (!writer)
|
||||||
|
p = p.render();
|
||||||
|
|
||||||
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -82,12 +82,12 @@ function TX(data, block, index) {
|
|||||||
|
|
||||||
if (data.inputs) {
|
if (data.inputs) {
|
||||||
for (i = 0; i < data.inputs.length; i++)
|
for (i = 0; i < data.inputs.length; i++)
|
||||||
this.inputs.push(new bcoin.input(data.inputs[i], this));
|
this.inputs.push(new bcoin.input(data.inputs[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.outputs) {
|
if (data.outputs) {
|
||||||
for (i = 0; i < data.outputs.length; i++)
|
for (i = 0; i < data.outputs.length; i++)
|
||||||
this.outputs.push(new bcoin.output(data.outputs[i], this));
|
this.outputs.push(new bcoin.output(data.outputs[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block && this.ts === 0) {
|
if (block && this.ts === 0) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user