From 3a652727c901cd7a7966f5513ee027f9e36c1da6 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 1 Jul 2016 05:42:04 -0700 Subject: [PATCH] script refactor. --- lib/bcoin/coins.js | 43 +++++---- lib/bcoin/miner.js | 2 +- lib/bcoin/output.js | 2 +- lib/bcoin/script.js | 229 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 205 insertions(+), 71 deletions(-) diff --git a/lib/bcoin/coins.js b/lib/bcoin/coins.js index 94fdfae9..b405b056 100644 --- a/lib/bcoin/coins.js +++ b/lib/bcoin/coins.js @@ -361,7 +361,6 @@ Coins.fromTX = function fromTX(tx) { * coin is just a pointer to that coin in the * Coins buffer, as well as a size. Parsing * is done only if that coin is being redeemed. - * @exports DeferredCoin * @constructor * @private * @param {Number} offset @@ -387,32 +386,34 @@ function DeferredCoin(offset, size, raw) { DeferredCoin.prototype.toCoin = function toCoin(coins, index) { var p = new BufferReader(this.raw); - var prefix, script, value, coin; - - p.seek(this.offset); - - prefix = p.readU8() & 3; - - if (prefix === 0) - script = bcoin.script.fromRaw(p.readVarBytes()); - else if (prefix === 1) - script = bcoin.script.fromPubkeyhash(p.readBytes(20)); - else if (prefix === 2) - script = bcoin.script.fromScripthash(p.readBytes(20)); - else - assert(false, 'Bad prefix.'); - - value = p.readVarint(); - - coin = new bcoin.coin(); + var coin = new bcoin.coin(); + var prefix; coin.version = coins.version; coin.coinbase = coins.coinbase; coin.height = coins.height; coin.hash = coins.hash; coin.index = index; - coin.script = script; - coin.value = value; + + p.seek(this.offset); + + prefix = p.readU8() & 3; + + switch (prefix) { + case 0: + coin.script.fromRaw(p.readVarBytes()); + break; + case 1: + coin.script.fromPubkeyhash(p.readBytes(20)); + break; + case 2: + coin.script.fromScripthash(p.readBytes(20)); + break; + default: + assert(false, 'Bad prefix.'); + } + + coin.value = p.readVarint(); return coin; }; diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index a5f0b4b5..1764148c 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -402,7 +402,7 @@ MinerBlock.prototype._init = function _init() { // Reward output. output = new bcoin.output(); - output.script = bcoin.script.fromAddress(this.address); + output.script.fromAddress(this.address); cb.outputs.push(output); diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index 9b1fbaea..ccd92725 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -52,7 +52,7 @@ Output.prototype.fromOptions = function fromOptions(options) { this.script.fromOptions(options.script); if (options.address) - this.script = bcoin.script.fromAddress(options.address); + this.script.fromAddress(options.address); return this; }; diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 772f8ede..fe5d20a0 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -90,6 +90,18 @@ Witness.prototype.toArray = function toArray() { return this.items.slice(); }; +/** + * Inject properties from an array of buffers. + * @private + * @param {Buffer[]} items + */ + +Witness.prototype.fromArray = function fromArray(items) { + assert(Array.isArray(items)); + this.items = items; + return this; +}; + /** * Insantiate witness from an array of buffers. * @param {Buffer[]} items @@ -97,7 +109,7 @@ Witness.prototype.toArray = function toArray() { */ Witness.fromArray = function fromArray(items) { - return new Witness(items); + return new Witness().fromArray(items); }; /** @@ -442,6 +454,14 @@ Witness.prototype.getString = function getString(i) { return item.toString('utf8'); }; +/** + * Clear the witness items. + */ + +Witness.prototype.clear = function clear() { + this.items.length = 0; +}; + /** * Set an item in the witness vector. * @param {Number} index @@ -749,6 +769,14 @@ Stack.prototype.top = function top(i) { return this.items[this.items.length + i]; }; +/** + * Clear the stack. + */ + +Stack.prototype.clear = function clear() { + this.items.length = 0; +}; + /** * Set stack item at index. * @param {Number} index @@ -1182,6 +1210,19 @@ Script.prototype.toArray = function toArray() { return code; }; +/** + * Inject properties from an array of + * of buffers and numbers. + * @private + */ + +Script.prototype.fromArray = function fromArray(code) { + assert(Array.isArray(code)); + this.code = Script.parseArray(code); + this.raw = Script.encode(this.code); + return this; +}; + /** * Instantiate script from an array * of buffers and numbers. @@ -1189,7 +1230,7 @@ Script.prototype.toArray = function toArray() { */ Script.fromArray = function fromArray(code) { - return new Script(code); + return new Script().fromArray(code); }; /** @@ -2434,6 +2475,17 @@ Script.isCode = function isCode(raw) { return true; }; +/** + * Inject properties from a pay-to-pubkey script. + * @private + * @param {Buffer} key + */ + +Script.prototype.fromPubkey = function fromPubkey(key) { + assert(Buffer.isBuffer(key) && key.length >= 33); + return this.fromArray([key, opcodes.OP_CHECKSIG]); +}; + /** * Create a pay-to-pubkey script. * @param {Buffer} key @@ -2441,19 +2493,18 @@ Script.isCode = function isCode(raw) { */ Script.fromPubkey = function fromPubkey(key) { - assert(Buffer.isBuffer(key) && key.length >= 33); - return Script.fromArray([key, opcodes.OP_CHECKSIG]); + return new Script().fromPubkey(key); }; /** - * Create a pay-to-pubkeyhash script. + * Inject properties from a pay-to-pubkeyhash script. + * @private * @param {Buffer} hash - * @returns {Script} */ -Script.fromPubkeyhash = function fromPubkeyhash(hash) { +Script.prototype.fromPubkeyhash = function fromPubkeyhash(hash) { assert(Buffer.isBuffer(hash) && hash.length === 20); - return Script.fromArray([ + return this.fromArray([ opcodes.OP_DUP, opcodes.OP_HASH160, hash, @@ -2463,14 +2514,24 @@ Script.fromPubkeyhash = function fromPubkeyhash(hash) { }; /** - * Create a pay-to-multisig script. - * @param {Number} m - * @param {Number} n - * @param {Buffer[]} keys + * Create a pay-to-pubkeyhash script. + * @param {Buffer} hash * @returns {Script} */ -Script.fromMultisig = function fromMultisig(m, n, keys) { +Script.fromPubkeyhash = function fromPubkeyhash(hash) { + return new Script().fromPubkeyhash(hash); +}; + +/** + * Inject properties from pay-to-multisig script. + * @private + * @param {Number} m + * @param {Number} n + * @param {Buffer[]} keys + */ + +Script.prototype.fromMultisig = function fromMultisig(m, n, keys) { var code = []; var i; @@ -2490,7 +2551,34 @@ Script.fromMultisig = function fromMultisig(m, n, keys) { code.push(n + 0x50); code.push(opcodes.OP_CHECKMULTISIG); - return Script.fromArray(code); + return this.fromArray(code); +}; + +/** + * Create a pay-to-multisig script. + * @param {Number} m + * @param {Number} n + * @param {Buffer[]} keys + * @returns {Script} + */ + +Script.fromMultisig = function fromMultisig(m, n, keys) { + return new Script().fromMultisig(m, n, keys); +}; + +/** + * Inject properties from a pay-to-scripthash script. + * @private + * @param {Buffer} hash + */ + +Script.prototype.fromScripthash = function fromScripthash(hash) { + assert(Buffer.isBuffer(hash) && hash.length === 20); + return this.fromArray([ + opcodes.OP_HASH160, + hash, + opcodes.OP_EQUAL + ]); }; /** @@ -2500,11 +2588,21 @@ Script.fromMultisig = function fromMultisig(m, n, keys) { */ Script.fromScripthash = function fromScripthash(hash) { - assert(Buffer.isBuffer(hash) && hash.length === 20); - return Script.fromArray([ - opcodes.OP_HASH160, - hash, - opcodes.OP_EQUAL + return new Script().fromScripthash(hash); +}; + +/** + * Inject properties from a nulldata/opreturn script. + * @private + * @param {Buffer} flags + */ + +Script.prototype.fromNulldata = function fromNulldata(flags) { + assert(Buffer.isBuffer(flags)); + assert(flags.length <= constants.script.MAX_OP_RETURN, 'Nulldata too large.'); + return this.fromArray([ + opcodes.OP_RETURN, + flags ]); }; @@ -2515,12 +2613,20 @@ Script.fromScripthash = function fromScripthash(hash) { */ Script.fromNulldata = function fromNulldata(flags) { - assert(Buffer.isBuffer(flags)); - assert(flags.length <= constants.script.MAX_OP_RETURN, 'Nulldata too large.'); - return Script.fromArray([ - opcodes.OP_RETURN, - flags - ]); + return new Script().fromNulldata(flags); +}; + +/** + * Inject properties from a witness program. + * @private + * @param {Number} version + * @param {Buffer} data + */ + +Script.prototype.fromProgram = function fromProgram(version, data) { + assert(utils.isNumber(version) && version >= 0 && version <= 16); + assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40); + return this.fromArray([version === 0 ? 0 : version + 0x50, data]); }; /** @@ -2531,9 +2637,32 @@ Script.fromNulldata = function fromNulldata(flags) { */ Script.fromProgram = function fromProgram(version, data) { - assert(utils.isNumber(version) && version >= 0 && version <= 16); - assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40); - return Script.fromArray([version === 0 ? 0 : version + 0x50, data]); + return new Script().fromProgram(version, data); +}; + +/** + * Inject properties from an address. + * @private + * @param {Address|Base58Address} address + */ + +Script.prototype.fromAddress = function fromAddress(address) { + if (typeof address === 'string') + address = bcoin.address.fromBase58(address); + + if (!address) + throw new Error('Unknown address type.'); + + if (address.type === 'pubkeyhash') + return this.fromPubkeyhash(address.hash); + + if (address.type === 'scripthash') + return this.fromScripthash(address.hash); + + if (address.version !== -1) + return this.fromProgram(address.version, address.hash); + + assert(false, 'Bad address type.'); }; /** @@ -2543,32 +2672,17 @@ Script.fromProgram = function fromProgram(version, data) { */ Script.fromAddress = function fromAddress(address) { - if (typeof address === 'string') - address = bcoin.address.fromBase58(address); - - if (!address) - throw new Error('Unknown address type.'); - - if (address.type === 'pubkeyhash') - return Script.fromPubkeyhash(address.hash); - - if (address.type === 'scripthash') - return Script.fromScripthash(address.hash); - - if (address.version !== -1) - return Script.fromProgram(address.version, address.hash); - - assert(false, 'Bad address type.'); + return new Script().fromAddress(address); }; /** - * Create a witness block commitment. + * Inject properties from a witness block commitment. + * @private * @param {Buffer} hash * @param {String|Buffer} flags - * @returns {Script} */ -Script.fromCommitment = function fromCommitment(hash, flags) { +Script.prototype.fromCommitment = function fromCommitment(hash, flags) { var p; if (!flags) @@ -2581,13 +2695,24 @@ Script.fromCommitment = function fromCommitment(hash, flags) { p.writeU32BE(0xaa21a9ed); p.writeHash(hash); - return Script.fromArray([ + return this.fromArray([ opcodes.OP_RETURN, p.render(), flags ]); }; +/** + * Create a witness block commitment. + * @param {Buffer} hash + * @param {String|Buffer} flags + * @returns {Script} + */ + +Script.fromCommitment = function fromCommitment(hash, flags) { + return new Script().fromCommitment(hash, flags); +}; + /** * Grab and deserialize the redeem script. * @returns {Script|null} Redeem script. @@ -3336,6 +3461,14 @@ Script.prototype.getString = function getString(i) { return op.data.toString('utf8'); }; +/** + * Clear the script code. + */ + +Script.prototype.clear = function clear() { + this.code.length = 0; +}; + /** * Set an item in the `code` array. * @param {Number} index