diff --git a/lib/bcoin.js b/lib/bcoin.js index be8aa950..354cca9c 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -77,7 +77,7 @@ bcoin.ec = require('./bcoin/ec'); bcoin.lru = require('./bcoin/lru'); bcoin.protocol = require('./bcoin/protocol'); bcoin.bloom = require('./bcoin/bloom'); -bcoin.script = require('./bcoin/script'); +bcoin.script = require('./bcoin/script2'); bcoin.input = require('./bcoin/input'); bcoin.output = require('./bcoin/output'); bcoin.coin = require('./bcoin/coin'); diff --git a/lib/bcoin/address.js b/lib/bcoin/address.js index 89fb5c44..23c6f18e 100644 --- a/lib/bcoin/address.js +++ b/lib/bcoin/address.js @@ -111,13 +111,12 @@ Address.prototype.getScript = function getScript() { assert(this.keys.length === this.n, 'Not all keys have been added.'); redeem = bcoin.script.createMultisig(this.keys, this.m, this.n); - redeem = bcoin.script.encode(redeem); if (this.witness) { - if (redeem.length > 10000) + if (redeem.getSize() > 10000) throw new Error('Redeem script too large (10000 byte limit).'); } else { - if (redeem.length > 520) + if (redeem.getSize() > 520) throw new Error('Redeem script too large (520 byte limit).'); } @@ -140,12 +139,12 @@ Address.prototype.getProgram = function getProgram() { 0, Address.hash160(this.getPublicKey())); } else if (this.type === 'multisig') { program = bcoin.script.createWitnessProgram( - 0, Address.sha256(this.getScript())); + 0, Address.sha256(this.getScript().encode())); } assert(program); - this._program = bcoin.script.encode(program); + this._program = program; return this._program; }; @@ -157,7 +156,7 @@ Address.prototype.getProgramHash = function getProgramHash() { if (this._programHash) return this._programHash; - this._programHash = Address.hash160(this.getProgram()); + this._programHash = Address.hash160(this.getProgram().encode()); return this._programHash; }; @@ -186,7 +185,7 @@ Address.prototype.getScriptHash160 = function getScriptHash256() { if (this._scriptHash160) return this._scriptHash160; - this._scriptHash160 = Address.hash160(this.getScript()); + this._scriptHash160 = Address.hash160(this.getScript().encode()); return this._scriptHash160; }; @@ -198,7 +197,7 @@ Address.prototype.getScriptHash256 = function getScriptHash256() { if (this._scriptHash256) return this._scriptHash256; - this._scriptHash256 = Address.sha256(this.getScript()); + this._scriptHash256 = Address.sha256(this.getScript().encode()); return this._scriptHash256; }; diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 234cdaaa..46a074db 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -215,7 +215,7 @@ Block.prototype._verify = function _verify() { }; Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() { - var coinbase, s, height; + var coinbase, code, height; if (this.version < 2) return -1; @@ -228,10 +228,10 @@ Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() { if (!coinbase || coinbase.inputs.length === 0) return -1; - s = coinbase.inputs[0].script; + code = coinbase.inputs[0].script.code; - if (Buffer.isBuffer(s[0]) && s[0].length <= 6) - height = new bn(s[0], 'le').toNumber(); + if (Buffer.isBuffer(code[0]) && code[0].length <= 6) + height = new bn(code[0], 'le').toNumber(); else height = -1; diff --git a/lib/bcoin/coin.js b/lib/bcoin/coin.js index 5d0968cf..b1739bba 100644 --- a/lib/bcoin/coin.js +++ b/lib/bcoin/coin.js @@ -60,7 +60,7 @@ function Coin(tx, index) { assert(typeof this.version === 'number'); assert(utils.isFinite(this.height)); assert(this.value instanceof bn); - assert(Array.isArray(this.script)); + assert(this.script instanceof bcoin.script); assert(typeof this.hash === 'string'); assert(utils.isFinite(this.index)); assert(typeof this.spent === 'boolean'); @@ -73,7 +73,7 @@ Coin.prototype.__defineGetter__('chain', function() { }); Coin.prototype.getSize = function getSize() { - return 4 + 4 + 8 + bcoin.script.getSize(this.script) + 32 + 4 + 1; + return 4 + 4 + 8 + this.script.getSize() + 32 + 4 + 1; }; Coin.prototype.getConfirmations = function getConfirmations(height) { @@ -136,7 +136,7 @@ Coin.prototype.toJSON = function toJSON() { version: this.version, height: this.height, value: utils.btc(this.value), - script: utils.toHex(bcoin.script.encode(this.script)), + script: utils.toHex(this.script.encode()), hash: utils.revHex(this.hash), index: this.index, spent: this.spent @@ -148,7 +148,7 @@ Coin._fromJSON = function _fromJSON(json) { version: json.version, height: json.height, value: utils.satoshi(json.value), - script: bcoin.script.decode(new Buffer(json.script, 'hex')), + script: new bcoin.script(new Buffer(json.script, 'hex')), hash: utils.revHex(json.hash), index: json.index, spent: json.spent diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index 0d7e4974..1c23e360 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -9,6 +9,7 @@ var bcoin = require('../bcoin'); var utils = bcoin.utils; var assert = utils.assert; var constants = bcoin.protocol.constants; +var Script = bcoin.script; /** * Input @@ -21,7 +22,7 @@ function Input(options, tx) { assert(typeof options.script !== 'string'); this.prevout = options.prevout; - this.script = options.script || []; + this.script = options.script || new Script([]); this.sequence = options.sequence == null ? 0xffffffff : options.sequence; this.witness = options.witness || []; this._size = options._size || 0; @@ -61,7 +62,7 @@ Input.prototype.getType = function getType() { type = bcoin.script.getInputType(this.witness, null, true); if (!type || type === 'unknown') - type = bcoin.script.getInputType(this.script); + type = this.script.getInputType(); if (!this._mutable) this._type = type; @@ -81,8 +82,10 @@ Input.prototype.getRedeem = function getRedeem() { return bcoin.script.getRedeem(this.witness); if (type === 'scripthash') { - redeem = bcoin.script.getRedeem(this.script); - if (bcoin.script.isWitnessScripthash(redeem)) + redeem = this.script.getRedeem(); + if (!redeem) + return; + if (redeem.isWitnessScripthash()) return bcoin.script.getRedeem(this.witness); return redeem; } @@ -99,7 +102,7 @@ Input.prototype.getSubtype = function getSubtype() { if (!redeem) return; - return bcoin.script.getOutputType(redeem); + return redeem.getType(); }; Input.prototype.getAddress = function getAddress() { @@ -118,7 +121,7 @@ Input.prototype.getAddress = function getAddress() { address = bcoin.script.getInputAddress(this.witness, null, true); if (!address) - address = bcoin.script.getInputAddress(this.script); + address = this.script.getInputAddress(); if (!this._mutable) this._address = address; @@ -142,13 +145,13 @@ Input.prototype.getLocktime = function getLocktime() { output = this.output; redeem = output.script; - if (bcoin.script.isScripthash(redeem)) - redeem = bcoin.script.getRedeem(this.script); + if (redeem.isScripthash()) + redeem = this.script.getRedeem(); if (redeem[1] !== 'checklocktimeverify') return; - return bcoin.script.getLocktime(redeem); + return redeem.getLocktime(); }; Input.prototype.isCoinbase = function isCoinbase() { @@ -177,51 +180,12 @@ Input.prototype.test = function test(addressTable) { return false; }; -Input.prototype.getSigops = function getSigops(scriptHash, accurate) { - var n = bcoin.script.getSigops(this.script, accurate); - if (scriptHash && !this.isCoinbase()) - n += bcoin.script.getScripthashSigops(this.script); - return n; -}; - Input.prototype.getID = function getID() { - var data = bcoin.script.encode(this.script); + var data = this.script.encode(); var hash = utils.toHex(utils.ripesha(data)); return '[' + this.type + ':' + hash.slice(0, 7) + ']'; }; -Input.prototype.getData = function getData() { - var def, data; - - assert(this instanceof Input); - - def = { - side: 'input', - value: new bn(0), - script: this.script, - sequence: this.sequence - }; - - def.prev = this.prevout.hash; - def.index = this.prevout.index; - - if (this.isCoinbase()) { - data = bcoin.script.getCoinbaseData(this.script); - return utils.merge(def, data, { - type: 'coinbase', - none: true - }); - } - - if (this.output) { - data = bcoin.script.getInputData(this.script, this.output.script); - data.value = this.output.value; - return utils.merge(def, data); - } - - return utils.merge(def, bcoin.script.getInputData(this.script)); -}; - Input.prototype.inspect = function inspect() { var redeem = this.getRedeem(); var output; @@ -262,7 +226,7 @@ Input.prototype.toJSON = function toJSON() { index: this.prevout.index }, output: this.output ? this.output.toJSON() : null, - script: utils.toHex(bcoin.script.encode(this.script)), + script: utils.toHex(this.script.encode()), witness: utils.toHex(bcoin.protocol.framer.witness(this.witness)), sequence: this.sequence }; @@ -275,7 +239,7 @@ Input._fromJSON = function _fromJSON(json) { index: json.prevout.index }, output: json.output ? bcoin.coin._fromJSON(json.output) : null, - script: bcoin.script.decode(new Buffer(json.script, 'hex')), + script: new Script(new Buffer(json.script, 'hex')), witness: bcoin.protocol.parser.parseWitness(new Buffer(json.witness, 'hex')), sequence: json.sequence }; diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 1bc74386..e868fffe 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -10,6 +10,7 @@ var bcoin = require('../bcoin'); var utils = bcoin.utils; var assert = utils.assert; var constants = bcoin.protocol.constants; +var Script = bcoin.script; /** * MTX @@ -140,7 +141,7 @@ MTX.prototype.addInput = function addInput(options, index) { input = bcoin.input(options, this); if (options.script) - input.script = options.script.slice(); + input.script = options.script.clone(); if (options.witness) input.witness = options.witness.slice(); @@ -166,7 +167,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { // Optimization: Don't bother with any below // calculation if the output is already templated. // Just say this is "our" output. - if (input.script.length || input.witness.length) + if (input.script.code.length || input.witness.length) return true; // Optimization: test output against the @@ -182,67 +183,67 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { // This is easily the hardest part about building a transaction // with segwit: figuring out where the redeem script and witness // redeem scripts go. - if (bcoin.script.isScripthash(prev)) { - if (addr.program && utils.isEqual(prev[1], addr.programHash)) { + if (prev.isScripthash()) { + if (addr.program && utils.isEqual(prev.code[1], addr.programHash)) { // Witness program nested in regular P2SH. - redeemScript = addr.program; + redeemScript = addr.program.encode(); vector = input.witness; dummy = new Buffer([]); - assert(addr.program[0] === 0, 'Non-zero version passed to address.'); - if (addr.program.length === 34) { + assert(addr.program.code[0] === 0, 'Non-zero version passed to address.'); + if (addr.program.code[1].length === 32) { // P2WSH nested within pay-to-scripthash // (it had to be this complicated, didn't it?) - witnessScript = addr.script; - prev = bcoin.script.decode(addr.script); - } else if (addr.program.length === 22) { + witnessScript = addr.script.encode(); + prev = addr.script; + } else if (addr.program.code[1].length === 20) { // P2WPKH nested within pay-to-scripthash. prev = bcoin.script.createPubkeyhash(addr.keyHash); } else { assert(false, 'Unknown program data length passed to address.'); } - } else if (addr.script && utils.isEqual(prev[1], addr.scriptHash160)) { + } else if (addr.script && utils.isEqual(prev.code[1], addr.scriptHash160)) { // Regular P2SH. - redeemScript = addr.script; - prev = bcoin.script.decode(addr.script); - vector = input.script; + redeemScript = addr.script.encode(); + vector = input.script.code; + prev = addr.script; dummy = 0; } else { return false; } - } else if (bcoin.script.isWitnessProgram(prev)) { + } else if (prev.isWitnessProgram()) { // Witness program. vector = input.witness; dummy = new Buffer([]); - if (prev[0] !== 0) + if (prev.code[0] !== 0) return false; - if (prev[1].length === 32) { + if (prev.code[1].length === 32) { // Bare P2WSH. - if (!addr.script || !utils.isEqual(prev[1], addr.scriptHash256)) + if (!addr.script || !utils.isEqual(prev.code[1], addr.scriptHash256)) return false; - witnessScript = addr.script; - prev = bcoin.script.decode(addr.script); - } else if (prev[1].length === 20) { + witnessScript = addr.script.encode(); + prev = addr.script; + } else if (prev.code[1].length === 20) { // Bare P2WPKH. - if (!utils.isEqual(prev[1], addr.keyHash)) + if (!utils.isEqual(prev.code[1], addr.keyHash)) return false; - prev = bcoin.script.createPubkeyhash(prev[1]); + prev = bcoin.script.createPubkeyhash(prev.code[1]); } else { // Bare... who knows? return false; } } else { // Wow, a normal output! Praise be to Jengus and Gord. - vector = input.script; + vector = input.script.code; dummy = 0; } - if (bcoin.script.isPubkey(prev)) { + if (prev.isPubkey()) { // P2PK - if (!utils.isEqual(prev[0], addr.publicKey)) + if (!utils.isEqual(prev.code[0], addr.publicKey)) return false; // Already has a script template (at least) @@ -250,9 +251,9 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { return true; vector[0] = dummy; - } else if (bcoin.script.isPubkeyhash(prev)) { + } else if (prev.isPubkeyhash()) { // P2PKH - if (!utils.isEqual(prev[2], addr.keyHash)) + if (!utils.isEqual(prev.code[2], addr.keyHash)) return false; // Already has a script template (at least) @@ -261,9 +262,9 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { vector[0] = dummy; vector[1] = addr.publicKey; - } else if (bcoin.script.isMultisig(prev)) { + } else if (prev.isMultisig()) { // Multisig - if (utils.indexOf(prev, addr.publicKey) === -1) + if (utils.indexOf(prev.code, addr.publicKey) === -1) return false; // Already has a script template (at least) @@ -276,13 +277,13 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { vector[0] = dummy; // Grab `n` value (number of keys). - n = prev[prev.length - 2]; + n = prev.code[prev.code.length - 2]; // Fill script with `n` signature slots. for (i = 0; i < n; i++) vector[i + 1] = dummy; } else { - if (utils.indexOf(prev, addr.publicKey) === -1) + if (utils.indexOf(prev.code, addr.publicKey) === -1) return false; // Already has a script template (at least) @@ -296,8 +297,8 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { vector[0] = dummy; // Fill script with `n` signature slots. - for (i = 0; i < prev.length; i++) { - if (bcoin.script.isKey(prev[i])) + for (i = 0; i < prev.code.length; i++) { + if (bcoin.script.isKey(prev.code[i])) vector[i + 1] = dummy; } } @@ -305,7 +306,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { // P2SH requires the redeem // script after signatures. if (redeemScript) - input.script.push(redeemScript); + input.script.code.push(redeemScript); // P2WSH requires the witness // script after signatures. @@ -316,7 +317,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { }; MTX.prototype.createSignature = function createSignature(index, prev, key, type, version) { - var prev, hash, signature; + var hash, signature; if (typeof index !== 'number') index = this.inputs.indexOf(index); @@ -357,15 +358,15 @@ MTX.prototype.signInput = function signInput(index, addr, type) { // Get the previous output's subscript prev = input.output.script; - vector = input.script; + vector = input.script.code; len = vector.length; dummy = 0; version = 0; // We need to grab the redeem script when // signing p2sh transactions. - if (bcoin.script.isScripthash(prev)) { - prev = bcoin.script.getRedeem(input.script); + if (prev.isScripthash()) { + prev = input.script.getRedeem(); len = vector.length - 1; } @@ -375,14 +376,14 @@ MTX.prototype.signInput = function signInput(index, addr, type) { // witnesses are stack items, so the `dummy` // _has_ to be an empty buffer (what OP_0 // pushes onto the stack). - if (bcoin.script.isWitnessScripthash(prev)) { + if (prev.isWitnessScripthash()) { prev = bcoin.script.getRedeem(input.witness); vector = input.witness; len = vector.length - 1; dummy = new Buffer([]); version = 1; - } else if (bcoin.script.isWitnessPubkeyhash(prev)) { - prev = bcoin.script.createPubkeyhash(prev[1]); + } else if (prev.isWitnessPubkeyhash()) { + prev = bcoin.script.createPubkeyhash(prev.code[1]); vector = input.witness; len = vector.length; dummy = new Buffer([]); @@ -393,7 +394,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) { signature = this.createSignature(index, prev, addr.key, type, version); // Add signatures. - if (bcoin.script.isPubkey(prev)) { + if (prev.isPubkey()) { // P2PK // Already signed. @@ -401,7 +402,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) { return true; // Make sure the pubkey is ours. - if (!utils.isEqual(addr.publicKey, prev[0])) + if (!utils.isEqual(addr.publicKey, prev.code[0])) return false; vector[0] = signature; @@ -409,7 +410,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) { return true; } - if (bcoin.script.isPubkeyhash(prev)) { + if (prev.isPubkeyhash()) { // P2PKH // Already signed. @@ -417,7 +418,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) { return true; // Make sure the pubkey hash is ours. - if (!utils.isEqual(addr.keyHash, prev[2])) + if (!utils.isEqual(addr.keyHash, prev.code[2])) return false; vector[0] = signature; @@ -425,18 +426,18 @@ MTX.prototype.signInput = function signInput(index, addr, type) { return true; } - if (bcoin.script.isMultisig(prev)) { + if (prev.isMultisig()) { // Multisig // Grab the redeem script's keys to figure // out where our key should go. - keys = prev.slice(1, -2); + keys = prev.code.slice(1, -2); // Grab `m` value (number of sigs required). - m = prev[0]; + m = prev.code[0]; // Grab `n` value (number of keys). - n = prev[prev.length - 2]; + n = prev.code[prev.code.length - 2]; } else { // Only allow non-standard signing for // scripthash. @@ -445,9 +446,9 @@ MTX.prototype.signInput = function signInput(index, addr, type) { keys = []; - for (i = 0; i < prev.length; i++) { - if (bcoin.script.isKey(prev[i])) - keys.push(prev[i]); + for (i = 0; i < prev.code.length; i++) { + if (bcoin.script.isKey(prev.code[i])) + keys.push(prev.code[i]); } // We don't know what m is, so @@ -573,7 +574,7 @@ MTX.prototype.addOutput = function addOutput(obj, value) { this.outputs.push(output); if (options.script) - output.script = options.script.slice(); + output.script = options.script.clone(); this.scriptOutput(this.outputs.length - 1, options); @@ -629,10 +630,7 @@ MTX.prototype.scriptOutput = function scriptOutput(index, options) { } else if (options.key) { // P2PK Transaction // [pubkey] checksig - script = [ - utils.ensureBuffer(options.key), - 'checksig' - ]; + script = bcoin.script.createPubkey(utils.ensureBuffer(options.key)); } else if (options.flags) { // Nulldata Transaction // return [data] @@ -648,13 +646,13 @@ MTX.prototype.scriptOutput = function scriptOutput(index, options) { // hash160 [hash] eq if (options.scriptHash) { if (options.locktime != null) { - script = [ + script = new Script([ bcoin.script.array(options.locktime), 'checklocktimeverify', 'drop' - ].concat(script); + ].concat(script.code)); } - hash = utils.ripesha(bcoin.script.encode(script)); + hash = utils.ripesha(bcoin.script.encode(script.code)); script = bcoin.script.createScripthash(hash); } @@ -668,7 +666,7 @@ MTX.prototype.maxSize = function maxSize(maxM, maxN) { // Create copy with 0-script inputs for (i = 0; i < copy.inputs.length; i++) { - copy.inputs[i].script = []; + copy.inputs[i].script = new Script([]); copy.inputs[i].witness = []; } @@ -687,50 +685,48 @@ MTX.prototype.maxSize = function maxSize(maxM, maxN) { // If we have access to the redeem script, // we can use it to calculate size much easier. - if (this.inputs[i].script.length && bcoin.script.isScripthash(prev)) { + if (this.inputs[i].script.code.length && prev.isScripthash()) { // Need to add the redeem script size // here since it will be ignored by // the isMultisig clause. // OP_PUSHDATA2 [redeem] - prev = this.inputs[i].script[this.inputs[i].script.length - 1]; - size += utils.sizeIntv(prev.length) + prev.length; - prev = bcoin.script.decode(prev); + prev = this.inputs[i].getRedeem(); + size += utils.sizeIntv(prev.getSize()) + prev.getSize(); } - if (bcoin.script.isWitnessProgram(prev)) { + if (prev.isWitnessProgram()) { witness = true; // Now calculating vsize. The regular // redeem script (if there was one) // is now worth 4 points. size *= 4; - if (this.inputs[i].witness.length && bcoin.script.isWitnessScripthash(prev)) { - prev = this.inputs[i].witness[this.inputs[i].witness.length - 1]; - size += utils.sizeIntv(prev.length) + prev.length; - prev = bcoin.script.decode(prev); - } else if (bcoin.script.isWitnessPubkeyhash(prev)) { - prev = bcoin.script.createPubkeyhash(prev[1]); + if (this.inputs[i].witness.length && prev.isWitnessScripthash()) { + prev = bcoin.script.getRedeem(this.inputs[i].witness); + size += utils.sizeIntv(prev.getSize()) + prev.getSize(); + } else if (prev.isWitnessPubkeyhash()) { + prev = bcoin.script.createPubkeyhash(prev.code[1]); } } - if (bcoin.script.isPubkey(prev)) { + if (prev.isPubkey()) { // P2PK // OP_PUSHDATA0 [signature] size += 1 + 73; - } else if (bcoin.script.isPubkeyhash(prev)) { + } else if (prev.isPubkeyhash()) { // P2PKH // OP_PUSHDATA0 [signature] size += 1 + 73; // OP_PUSHDATA0 [key] size += 1 + 33; - } else if (bcoin.script.isMultisig(prev)) { + } else if (prev.isMultisig()) { // Bare Multisig // Get the previous m value: - m = prev[0]; + m = prev.code[0]; // OP_0 size += 1; // OP_PUSHDATA0 [signature] ... size += (1 + 73) * m; - } else if (bcoin.script.isScripthash(prev)) { + } else if (prev.isScripthash()) { // P2SH Multisig // This technically won't work well for other // kinds of P2SH. It will also over-estimate @@ -760,8 +756,8 @@ MTX.prototype.maxSize = function maxSize(maxM, maxN) { size += 1; } else { // OP_PUSHDATA0 [signature] - for (j = 0; j < prev.length; j++) { - if (bcoin.script.isKey(prev[j])) + for (j = 0; j < prev.code.length; j++) { + if (bcoin.script.isKey(prev.code[j])) size += 1 + 73; } } @@ -986,8 +982,8 @@ MTX.prototype.sortMembers = function sortMembers() { if (res !== 0) return res; - a = bcoin.script.encode(a.script); - b = bcoin.script.encode(b.script); + a = a.encode(); + b = b.encode(); return utils.cmp(a, b); }); diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index 0faa143b..00928e9b 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -9,6 +9,7 @@ var bcoin = require('../bcoin'); var utils = bcoin.utils; var assert = utils.assert; var constants = bcoin.protocol.constants; +var Script = bcoin.script; /** * Output @@ -30,7 +31,7 @@ function Output(options, tx) { } this.value = utils.satoshi(value || new bn(0)); - this.script = options.script || []; + this.script = options.script || new Script([]); this._size = options._size || 0; this._offset = options._offset || 0; this._mutable = !tx || (tx instanceof bcoin.mtx); @@ -57,7 +58,7 @@ Output.prototype.getType = function getType() { if (this._type) return this._type; - type = bcoin.script.getOutputType(this.script); + type = this.script.getType(); if (!this._mutable) this._type = type; @@ -71,7 +72,7 @@ Output.prototype.getAddress = function getAddress() { if (this._address) return this._address; - address = bcoin.script.getOutputAddress(this.script); + address = this.script.getAddress(); if (!this._mutable) this._address = address; @@ -101,28 +102,12 @@ Output.prototype.test = function test(addressTable) { return false; }; -Output.prototype.getSigops = function getSigops(accurate) { - return bcoin.script.getSigops(this.script, accurate); -}; - Output.prototype.getID = function getID() { - var data = bcoin.script.encode(this.script); + var data = this.script.encode(); var hash = utils.toHex(utils.ripesha(data)); return '[' + this.type + ':' + hash.slice(0, 7) + ']'; }; -Output.prototype.getData = function getData() { - var def; - - def = { - side: 'output', - value: this.value, - script: this.script - }; - - return utils.merge(def, bcoin.script.getOutputData(this.script)); -}; - Output.prototype.inspect = function inspect() { return { type: this.getType(), @@ -135,14 +120,14 @@ Output.prototype.inspect = function inspect() { Output.prototype.toJSON = function toJSON() { return { value: utils.btc(this.value), - script: utils.toHex(bcoin.script.encode(this.script)) + script: utils.toHex(this.script.encode()) }; }; Output._fromJSON = function _fromJSON(json) { return { value: utils.satoshi(json.value), - script: bcoin.script.decode(new Buffer(json.script, 'hex')) + script: new Script(new Buffer(json.script, 'hex')) }; }; diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index 06ca41b5..078e03cf 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -380,18 +380,16 @@ Framer._getBlocks = function _getBlocks(hashes, stop) { Framer.input = function _input(input) { var off = 0; - var s, p; + var script, p; - s = bcoin.script.encode(input.script); - p = new Buffer(32 + 4 + utils.sizeIntv(s.length) + s.length + 4); + script = input.script.encode(); + p = new Buffer(32 + 4 + utils.sizeIntv(script.length) + script.length + 4); off += utils.copy(new Buffer(input.prevout.hash, 'hex'), p, off); off += utils.writeU32(p, input.prevout.index, off); - off += utils.writeIntv(p, s.length, off); - if (!s.copy) - console.log(s); - off += utils.copy(s, p, off); + off += utils.writeIntv(p, script.length, off); + off += utils.copy(script, p, off); off += utils.writeU32(p, input.sequence, off); @@ -400,23 +398,23 @@ Framer.input = function _input(input) { Framer.output = function _output(output) { var off = 0; - var s, p; + var script, p; - s = bcoin.script.encode(output.script); - p = new Buffer(8 + utils.sizeIntv(s.length) + s.length); + script = output.script.encode(); + p = new Buffer(8 + utils.sizeIntv(script.length) + script.length); off += utils.write64(p, output.value, off); assert(output.value.byteLength() <= 8); - off += utils.writeIntv(p, s.length, off); - off += utils.copy(s, p, off); + off += utils.writeIntv(p, script.length, off); + off += utils.copy(script, p, off); return p; }; Framer.utxo = Framer.coin = function _coin(coin, extended) { - var script = bcoin.script.encode(coin.script); + var script = coin.script.encode(); var intSize = utils.sizeIntv(script.length); var height = coin.height; var off = 0; diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index 2b21a681..d249334a 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -356,7 +356,7 @@ Parser.parseBlockCompact = function parseBlockCompact(p) { var height = -1; var version, prevBlock, merkleRoot, ts, bits, nonce; var i, totalTX, tx; - var inCount, input, s, raw; + var inCount, input, raw; p = new BufferReader(p); p.start(); @@ -379,9 +379,8 @@ Parser.parseBlockCompact = function parseBlockCompact(p) { } if (input) { - s = input.script; - if (Buffer.isBuffer(s[0])) - height = s[0]; + if (Buffer.isBuffer(input.script.code[0])) + height = input.script.code[0]; } raw = p.data; @@ -411,7 +410,7 @@ Parser.parseInput = function parseInput(p) { hash = p.readHash(); index = p.readU32(); - script = bcoin.script.decode(p.readVarBytes()); + script = new bcoin.script(p.readVarBytes()); sequence = p.readU32(); return { @@ -432,7 +431,7 @@ Parser.parseOutput = function parseOutput(p) { p.start(); value = p.read64(); - script = bcoin.script.decode(p.readVarBytes()); + script = new bcoin.script(p.readVarBytes()); return { _size: p.end(), @@ -443,6 +442,7 @@ Parser.parseOutput = function parseOutput(p) { Parser.parseCoin = function parseCoin(p, extended) { var version, height, value, script, hash, index, spent; + p = new BufferReader(p); version = p.readU32(); @@ -453,12 +453,12 @@ Parser.parseCoin = function parseCoin(p, extended) { value = p.read64(); - script = bcoin.script.decode(p.readVarBytes()); + script = new bcoin.script(p.readVarBytes()); if (extended) { hash = p.readHash(); index = p.readU32(); - spent = p.readU8(); + spent = p.readU8() === 1; } else { hash = utils.slice(constants.zeroHash); index = 0xffffffff; @@ -527,7 +527,7 @@ Parser.isWitnessTX = function isWitnessTX(p) { if (p.left() < 12) return false; - return p[p.offset + 4] === 0 && p[p.offset + 5] !== 0; + return p.data[p.offset + 4] === 0 && p.data[p.offset + 5] !== 0; }; Parser.parseWitnessTX = function parseWitnessTX(p) { @@ -822,7 +822,7 @@ BufferReader.prototype.read64BE = function read64BE() { }; BufferReader.prototype.readBytes = function readBytes(size) { - assert(size > 0) + assert(size >= 0); assert(this.offset + size <= this.data.length); var ret = utils.slice(this.data, this.offset, this.offset + size); this.offset += size; @@ -830,7 +830,7 @@ BufferReader.prototype.readBytes = function readBytes(size) { }; BufferReader.prototype.readString = function readString(enc, size) { - assert(size > 0) + assert(size >= 0); assert(this.offset + size <= this.data.length); var ret = this.data.toString(enc, this.offset, this.offset + size); this.offset += size; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 7e1b213a..51c3eba5 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -10,6 +10,7 @@ var bcoin = require('../bcoin'); var utils = bcoin.utils; var assert = utils.assert; var constants = bcoin.protocol.constants; +var Script = bcoin.script; /** * TX @@ -172,19 +173,6 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) { return -1; }; -TX.prototype.getSubscript = function getSubscript(index) { - var script; - - if (typeof index !== 'number') - index = this.outputs.indexOf(index); - - assert(this.outputs[index]); - - script = this.outputs[index].script; - - return bcoin.script.getSubscript(script); -}; - TX.prototype.signatureHash = function signatureHash(index, s, type, version) { assert(version >= 0 && version <= 1); if (version === 0) @@ -206,7 +194,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) { for (i = 0; i < this.inputs.length; i++) { copy.inputs.push({ prevout: this.inputs[i].prevout, - script: this.inputs[i].script.slice(), + script: this.inputs[i].script.clone(), witness: this.inputs[i].witness.slice(), sequence: this.inputs[i].sequence }); @@ -215,7 +203,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) { for (i = 0; i < this.outputs.length; i++) { copy.outputs.push({ value: this.outputs[i].value, - script: this.outputs[i].script.slice() + script: this.outputs[i].script.clone() }); } @@ -226,7 +214,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) { type = constants.hashType[type]; assert(index >= 0 && index < copy.inputs.length) - assert(Array.isArray(s)); + assert(s instanceof bcoin.script); // Disable this for now. We allow null hash types // because bitcoind allows empty signatures. On @@ -236,7 +224,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) { // Remove all signatures. for (i = 0; i < copy.inputs.length; i++) - copy.inputs[i].script = []; + copy.inputs[i].script = new Script([]); // Set our input to previous output's script copy.inputs[index].script = s; @@ -262,7 +250,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) { // Null outputs that are not the at current input index. for (i = 0; i < copy.outputs.length; i++) { if (i !== index) { - copy.outputs[i].script = []; + copy.outputs[i].script = new Script([]); copy.outputs[i].value = new bn('ffffffffffffffff', 'hex'); } } @@ -302,7 +290,7 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, s, type) { type = constants.hashType[type]; assert(index >= 0 && index < this.inputs.length) - assert(Array.isArray(s)); + assert(s instanceof bcoin.script); if (!(type & constants.hashType.anyonecanpay)) { hashPrevouts = new Buffer(36 * this.inputs.length); @@ -353,7 +341,7 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, s, type) { hashOutputs.fill(0); } - s = bcoin.script.encode(s); + s = s.encode(); msg = new Buffer( 4 + 32 + 32 + 36 @@ -666,23 +654,23 @@ TX.prototype._getSigops = function _getSigops(scriptHash, accurate) { prev = input.output.script; - total += bcoin.script.getSigops(input.script, accurate); + total += input.script.getSigops(accurate); if (scriptHash && !this.isCoinbase()) { - if (!bcoin.script.isScripthash(prev)) + if (!prev.isScripthash()) return; - if (!bcoin.script.isPushOnly(input.script)) + if (!input.script.isPushOnly()) return; - prev = bcoin.script.getRedeem(input.script); + prev = input.script.getRedeem(); - total += bcoin.script.getSigops(prev, true); + total += prev.getSigops(true); } }, this); this.outputs.forEach(function(output) { - total += bcoin.script.getSigops(output.script, accurate); + total += output.script.getSigops(accurate); }, this); return total; @@ -699,19 +687,19 @@ TX.prototype.getSigops = function getSigops(scriptHash, accurate) { prev = input.output.script; - if (bcoin.script.isScripthash(prev)) - prev = bcoin.script.getRedeem(input.script); + if (prev.isScripthash()) + prev = input.script.getRedeem(); - if (bcoin.script.isWitnessScripthash(prev)) { + if (prev.isWitnessScripthash()) { prev = bcoin.script.getRedeem(input.witness); - cost += bcoin.script.getSigops(prev, true); + cost += prev.getSigops(true); } else { cost += 0; } }, this); this.outputs.forEach(function(output) { - if (bcoin.script.isWitnessPubkeyhash(output.script)) + if (output.script.isWitnessPubkeyhash()) cost += 1; else cost += 0; @@ -736,7 +724,7 @@ TX.prototype.isStandard = function isStandard(flags) { for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; - if (bcoin.script.getSize(input.script) > 1650) + if (input.script.getSize() > 1650) return false; // Not accurate? @@ -744,16 +732,16 @@ TX.prototype.isStandard = function isStandard(flags) { continue; if (flags & constants.flags.VERIFY_SIGPUSHONLY) { - if (!bcoin.script.isPushOnly(input.script)) + if (!input.script.isPushOnly()) return false; } } for (i = 0; i < this.outputs.length; i++) { output = this.outputs[i]; - type = bcoin.script.getType(output.script); + type = output.script.getType(); - if (!bcoin.script.isStandard(output.script)) + if (!output.script.isStandard()) return false; if (type === 'unknown') @@ -793,7 +781,7 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) { if (!input.output) return false; - args = bcoin.script.getArgs(input.output.script); + args = input.output.script.getArgs(); if (args < 0) return false; @@ -803,10 +791,10 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) { // Bitcoind doesn't do this, but it's possible someone // could DoS us by sending ridiculous txs to the mempool // if we don't put this here. - if (!bcoin.script.isPushOnly(input.script)) + if (!input.script.isPushOnly()) return false; - res = bcoin.script.execute(input.script, stack, this, i, flags); + res = input.script.execute(stack, this, i, flags); // TODO: Segwit here. @@ -814,29 +802,27 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) { return false; if ((flags & constants.flags.VERIFY_P2SH) - && bcoin.script.isScripthash(input.output.script)) { + && input.output.script.isScripthash()) { if (stack.length === 0) return false; - redeem = stack.pop(); + redeem = bcoin.script.getRedeem(stack); - if (!Buffer.isBuffer(redeem)) + if (!redeem) return false; // Not accurate? - if (redeem.length > 520) + if (redeem.getSize() > 520) return false; - redeem = bcoin.script.decode(redeem); - // Also consider scripthash "unknown"? - if (bcoin.script.getType(redeem) === 'unknown') { - if (bcoin.script.getSigops(redeem, true) > maxSigops) + if (redeem.getType() === 'unknown') { + if (redeem.getSigops(true) > maxSigops) return false; continue; } - targs = bcoin.script.getArgs(redeem); + targs = redeem.getArgs(); if (targs < 0) return false; args += targs; @@ -942,11 +928,11 @@ TX.prototype.getValue = function getValue() { TX.prototype.hasType = function hasType(type) { for (var i = 0; i < this.inputs.length; i++) { - if (bcoin.script.getInputType(this.inputs[i].script) === type) + if (this.inputs[i].getInputType() === type) return true; } for (var i = 0; i < this.outputs.length; i++) { - if (bcoin.script.getType(this.outputs[i].script) === type) + if (this.outputs[i].getType() === type) return true; } return false; diff --git a/test/protocol-test.js b/test/protocol-test.js index 478e51a8..9665e86a 100644 --- a/test/protocol-test.js +++ b/test/protocol-test.js @@ -179,6 +179,6 @@ describe('Protocol', function() { 'de5c0500000017a9141d9ca71efa36d814424ea6ca1437e67287aebe348' + '700000000', 'hex'); var tx = bcoin.protocol.parser.parseTX(rawTwoTxs); - assert.deepEqual(tx._raw, rawFirstTx); + assert.deepEqual(bcoin.protocol.framer.tx(tx), rawFirstTx); }); }); diff --git a/test/script-test.js b/test/script-test.js index 7239af4e..72524869 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -1,5 +1,6 @@ var assert = require('assert'); var bcoin = require('../'); +var Script = bcoin.script; describe('Script', function() { it('should encode/decode script', function() { @@ -34,55 +35,55 @@ describe('Script', function() { it('should recognize a P2SH output', function () { var hex = 'a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87' var encoded = new Buffer(hex, 'hex') - var decoded = bcoin.script.decode(encoded); - assert(bcoin.script.isScripthash(decoded)) + var decoded = new bcoin.script(encoded); + assert(decoded.isScripthash()) }) it('should recognize a Null Data output', function () { var hex = '6a28590c080112220a1b353930632e6f7267282a5f5e294f7665726c6179404f7261636c65103b1a010c' var encoded = new Buffer(hex, 'hex') - var decoded = bcoin.script.decode(encoded); - assert(bcoin.script.isNulldata(decoded)) + var decoded = new Script(encoded); + assert(decoded.isNulldata()) }) it('should handle if statements correctly', function () { - var inputScript = [1, 2]; - var prevOutScript = [2, 'equal', 'if', 3, 'else', 4, 'endif', 5]; + var inputScript = new Script([1, 2]); + var prevOutScript = new Script([2, 'equal', 'if', 3, 'else', 4, 'endif', 5]); var stack = []; - bcoin.script.execute(inputScript, stack); - var res = bcoin.script.execute(prevOutScript, stack); + inputScript.execute(stack); + var res = prevOutScript.execute(stack); assert(res); assert.deepEqual(stack.slice(), [[1], [3], [5]]); - var inputScript = [1, 2]; - var prevOutScript = [9, 'equal', 'if', 3, 'else', 4, 'endif', 5]; + var inputScript = new Script([1, 2]); + var prevOutScript = new Script([9, 'equal', 'if', 3, 'else', 4, 'endif', 5]); var stack = []; - bcoin.script.execute(inputScript, stack); - var res = bcoin.script.execute(prevOutScript, stack); + inputScript.execute(stack); + var res = prevOutScript.execute(stack); assert(res); assert.deepEqual(stack.slice(), [[1], [4], [5]]); - var inputScript = [1, 2]; - var prevOutScript = [2, 'equal', 'if', 3, 'endif', 5]; + var inputScript = new Script([1, 2]); + var prevOutScript = new Script([2, 'equal', 'if', 3, 'endif', 5]); var stack = []; - bcoin.script.execute(inputScript, stack); - var res = bcoin.script.execute(prevOutScript, stack); + inputScript.execute(stack); + var res = prevOutScript.execute(stack); assert(res); assert.deepEqual(stack.slice(), [[1], [3], [5]]); - var inputScript = [1, 2]; - var prevOutScript = [9, 'equal', 'if', 3, 'endif', 5]; + var inputScript = new Script([1, 2]); + var prevOutScript = new Script([9, 'equal', 'if', 3, 'endif', 5]); var stack = []; - bcoin.script.execute(inputScript, stack); - var res = bcoin.script.execute(prevOutScript, stack); + inputScript.execute(stack); + var res = prevOutScript.execute(stack); assert(res); assert.deepEqual(stack.slice(), [[1], [5]]); - var inputScript = [1, 2]; - var prevOutScript = [9, 'equal', 'notif', 3, 'endif', 5]; + var inputScript = new Script([1, 2]); + var prevOutScript = new Script([9, 'equal', 'notif', 3, 'endif', 5]); var stack = []; - bcoin.script.execute(inputScript, stack); - var res = bcoin.script.execute(prevOutScript, stack); + inputScript.execute(stack); + var res = prevOutScript.execute(stack); assert(res); assert.deepEqual(stack.slice(), [[1], [3], [5]]); }) diff --git a/test/wallet-test.js b/test/wallet-test.js index 1bb54081..3b32a155 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -13,12 +13,12 @@ var dummyInput = { version: 1, height: 0, value: constants.maxMoney.clone(), - script: [], + script: new bcoin.script([]), hash: constants.zeroHash, index: 0, spent: false }, - script: [], + script: new bcoin.script([]), sequence: 0xffffffff }; @@ -167,7 +167,7 @@ describe('Wallet', function() { // Script inputs but do not sign w.scriptInputs(fake); // Fake signature - fake.inputs[0].script[0] = new Buffer([0,0,0,0,0,0,0,0,0]); + fake.inputs[0].script.code[0] = new Buffer([0,0,0,0,0,0,0,0,0]); // balance: 11000 // Just for debugging @@ -459,6 +459,8 @@ describe('Wallet', function() { send.addOutput({ address: receive.getAddress(), value: 5460 }); assert(!send.verify(null, true, flags)); w1.fill(send, { m: w1.m, n: w1.n }, function(err) { + if (err) + throw err; assert(!err); w1.sign(send); @@ -499,7 +501,7 @@ describe('Wallet', function() { if (witness) send.inputs[0].witness[2] = new Buffer([]); else - send.inputs[0].script[2] = 0; + send.inputs[0].script.code[2] = 0; assert(!send.verify(null, true, flags)); assert.equal(send.getFee().toNumber(), 10000);