diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index b4da3244..a5e5fecd 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -154,8 +154,8 @@ Block.prototype.__defineGetter__('commitmentHash', function() { for (i = 0; i < coinbase.outputs.length; i++) { commitment = coinbase.outputs[i].script; - if (bcoin.script.isCommitment(commitment)) { - commitmentHash = bcoin.script.getCommitmentHash(commitment); + if (commitment.isCommitment()) { + commitmentHash = commitment.getCommitmentHash(); break; } } diff --git a/lib/bcoin/script2.js b/lib/bcoin/script2.js index afb53420..e8c3134f 100644 --- a/lib/bcoin/script2.js +++ b/lib/bcoin/script2.js @@ -63,11 +63,11 @@ Witness.prototype.isPubkeyhashInput = function isPubkeyhashInput(hash) { }; Witness.prototype.isMultisigInput = function isMultisigInput(keys) { - return Script.isMultisigInput(this.items, keys); + return Script.isMultisigInput(this.items, keys, true); }; Witness.prototype.isScripthashInput = function isScripthashInput(redeem) { - return Script.isScripthashInput(this.items, redeem); + return Script.isScripthashInput(this.items, redeem, true); }; Witness.prototype.getRedeem = function getRedeem() { @@ -77,6 +77,53 @@ Witness.prototype.getRedeem = function getRedeem() { return this.redeem; }; +Witness.fromString = function fromString(items) { + var i, op; + + items = items.trim().split(/\s+/); + + // Remove OP_ prefixes and lowercase + for (i = 0; i < items.length; i++) { + op = items[i]; + if (typeof op === 'string') { + op = op.toLowerCase(); + if (op.indexOf('op_') === 0) + op = op.slice(3); + } + items[i] = op; + } + + // Convert OP_FALSE to 0, convert OP_1-OP_16 + // to number literals, convert -1 to OP_1NEGATE. + // Convert hex strings to arrays. + for (i = 0; i < items.length; i++) { + op = items[i]; + + if (op === '-1' || op === '1negate') { + op = new Buffer([0xff]); + } else if (op === '0' || op === 'false') { + op = new Buffer([]); + } else if (op === 'true') { + op = new Buffer([1]); + } else if (+op >= 1 && +op <= 16) { + op = new Buffer([+op]); + } else if (constants.opcodes[op] == null) { + if (op[0] === '[') + op = op.slice(1, -1); + if (op.indexOf('0x') === 0) + op = op.substring(2); + assert(utils.isHex(op), 'Non hex-string.'); + op = new Buffer(op, 'hex'); + } else { + assert(false, 'Non-stack item in witness string.'); + } + + items[i] = op; + } + + return new Witness(items); +}; + function Stack(items) { this.items = items || []; this.alt = []; @@ -1360,10 +1407,10 @@ Script.prototype.getInputAddress = function getInputAddress(prev, isWitness) { isWitness ? 'witnesspubkeyhash' : 'pubkeyhash'); } - if (this.isMultisigInput()) + if (this.isMultisigInput(null, isWitness)) return; - if (this.isScripthashInput()) { + if (this.isScripthashInput(null, isWitness)) { return bcoin.address.compileData(this.code[this.code.length - 1], isWitness ? 'witnessscripthash' : 'scripthash'); } @@ -1627,8 +1674,8 @@ Script.getInputType = function getInputType(code, prev, isWitness) { type = (Script.isPubkeyInput(code) && 'pubkey') || (Script.isPubkeyhashInput(code) && 'pubkeyhash') - || (Script.isMultisigInput(code) && 'multisig') - || (Script.isScripthashInput(code) && 'scripthash') + || (Script.isMultisigInput(code, null, isWitness) && 'multisig') + || (Script.isScripthashInput(code, null, isWitness) && 'scripthash') || 'unknown'; if (isWitness) { @@ -1684,21 +1731,26 @@ Script.prototype.isMultisigInput = function isMultisigInput(keys) { return Script.isMultisigInput(this.code, keys); }; -Script.isMultisigInput = function isMultisigInput(code, keys) { +Script.isMultisigInput = function isMultisigInput(code, keys, isWitness) { var i; // We need to rule out scripthash because // it may look like multisig. This is // strange because it's technically a // recursive call. - if (this.isScripthashInput()) + if (Script.isScripthashInput(null, isWitness)) return false; if (code.length < 3) return false; - if (code[0] !== 0 && !script.isDummy(code[0])) - return false; + if (isWitness) { + if (!script.isDummy(code[0])) + return false; + } else { + if (code[0] !== 0) + return false; + } for (i = 1; i < code.length; i++) { if (!Script.isSignature(code[i])) @@ -1714,7 +1766,7 @@ Script.prototype.isScripthashInput = function isScripthashInput(redeem) { return Script.isScripthashInput(this.code, redeem); }; -Script.isScripthashInput = function isScripthashInput(code, redeem) { +Script.isScripthashInput = function isScripthashInput(code, redeem, isWitness) { var raw; // Grab the raw redeem script. @@ -1722,9 +1774,13 @@ Script.isScripthashInput = function isScripthashInput(code, redeem) { // Need at least one data element with // the redeem script. NOTE: NOT THE CASE FOR SEGWIT! - // if (code.length < 2) - if (code.length < 1) - return false; + if (isWitness) { + if (code.length < 1) + return false; + } else { + if (code.length < 2) + return false; + } // Last data element should be an array // for the redeem script. @@ -2144,93 +2200,49 @@ Script.prototype.getArgs = function getArgs() { return -1; }; -// Witnesses aren't scripts, but we still -// want to convert [0] to OP_0, [0xff] to 1negate, etc. -Script.decodeWitness = function decodeWitness(witness) { - var s = []; - var chunk, i; - - for (i = 0; i < witness.length; i++) { - chunk = witness[i]; - - if (chunk.length <= 1) { - if (chunk.length === 0) - op = new Buffer([]); - else if (chunk[0] >= 1 && chunk <= 16) - op = chunk[0]; - else if (chunk[0] === 0xff) - op = '1negate'; - } - - s.push(op); - } - - return s; -}; - -Script.encodeWitness = function encodeWitness(s) { - var witness = []; - var chunk, i; - - for (i = 0; i < s.length; i++) { - chunk = s[i]; - - if (chunk === 0) - chunk = new Buffer([]); - else if (chunk >= 1 && chunk <= 16) - chunk = new Buffer([chunk]); - else if (chunk === '1negate') - chunk = new Buffer([0xff]); - - assert(Buffer.isBuffer(chunk)); - - witness.push(chunk); - } - - return witness; -}; - -Script.fromShittyString = function fromShittyString(s) { +Script.fromString = function fromString(code) { var i, op; - s = s.split(/\s+/); + code = code.trim().split(/\s+/); // Remove OP_ prefixes and lowercase - for (i = 0; i < s.length; i++) { - op = s[i]; + for (i = 0; i < code.length; i++) { + op = code[i]; if (typeof op === 'string') { op = op.toLowerCase(); if (op.indexOf('op_') === 0) op = op.slice(3); } - s[i] = op; + code[i] = op; } - // Convert OP_0 to array, convert OP_1-OP_16 + // Convert OP_FALSE to 0, convert OP_1-OP_16 // to number literals, convert -1 to OP_1NEGATE. // Convert hex strings to arrays. - for (i = 0; i < s.length; i++) { - op = s[i]; + for (i = 0; i < code.length; i++) { + op = code[i]; - if (op === '-1' || op === -1) + if (op === '-1') { op = '1negate'; - else if (op === '0' || op === 0 || op === 'false') - op = []; - else if (op === 'true') + } else if (op === '0' || op === 'false') { + op = 0; + } else if (op === 'true') { op = 1; - else if (+op >= 1 && +op <= 16) + } else if (+op >= 1 && +op <= 16) { op = +op; - - if (typeof op === 'string' && constants.opcodes[op] == null) { + } else if (constants.opcodes[op] == null) { if (op[0] === '[') op = op.slice(1, -1); + if (op.indexOf('0x') === 0) + op = op.substring(2); + assert(utils.isHex(op), 'Non hex-string.'); op = new Buffer(op, 'hex'); } - s[i] = op; + code[i] = op; } - return new Script(s); + return new Script(code); }; Script.verify = function verify(input, witness, output, tx, i, flags) {