diff --git a/lib/bcoin.js b/lib/bcoin.js index 430ba710..ea3dcd73 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -12,6 +12,8 @@ bcoin.utils = require('./bcoin/utils'); bcoin.bloom = require('./bcoin/bloom'); bcoin.protocol = require('./bcoin/protocol'); bcoin.script = require('./bcoin/script'); +bcoin.input = require('./bcoin/input'); +bcoin.output = require('./bcoin/output'); bcoin.tx = require('./bcoin/tx'); bcoin.txPool = require('./bcoin/tx-pool'); bcoin.block = require('./bcoin/block'); diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js new file mode 100644 index 00000000..1eff13f7 --- /dev/null +++ b/lib/bcoin/input.js @@ -0,0 +1,91 @@ +/** + * input.js - input object for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * https://github.com/indutny/bcoin + */ + +var bn = require('bn.js'); +var bcoin = require('../bcoin'); +var utils = bcoin.utils; + +/** + * Input + */ + +function Input(options) { + var tx = options.tx; + var lock; + + if (!(this instanceof Input)) + return new Input(options); + + if (!tx) + throw new Error('No TX passed into Input.'); + + this.out = { + tx: options.out.tx || null, + hash: options.out.hash || null, + index: options.out.index + }; + + this.script = options.script ? options.script.slice() : []; + this.seq = options.seq === undefined ? 0xffffffff : options.seq; + + if (options.script && options.script._raw) + utils.hidden(this.script, '_raw', options.script._raw); + + if (this.output) { + lock = this.lock; + if (lock >= 0) { + if (tx._lock === 0) + tx.lock = Math.max(lock, tx.lock); + if (!bcoin.script.spendable(this.output.script, tx.lock)) + throw new Error('Cannot spend ' + utils.revHex(this.out.hash)); + } + } + + if (tx.lock !== 0) { + if (options.seq === undefined) + this.seq = 0; + } +} + +Input.prototype.__defineGetter__('data', function() { + return bcoin.tx.getInputData(this); +}); + +Input.prototype.__defineGetter__('addr', function() { + return this.data.addr; +}); + +Input.prototype.__defineGetter__('type', function() { + return this.data.type; +}); + +Input.prototype.__defineGetter__('lock', function() { + if (!this.output) + return; + return this.output.lock; +}); + +Input.prototype.__defineGetter__('output', function() { + if (!this.out.tx) + return; + return this.out.tx.outputs[this.out.index]; +}); + +Input.prototype.__defineGetter__('value', function() { + if (!this.output) + return; + return this.output.value; +}); + +Input.prototype.__defineGetter__('tx', function() { + return this.out.tx; +}); + +/** + * Expose + */ + +module.exports = Input; diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js new file mode 100644 index 00000000..3a75ee03 --- /dev/null +++ b/lib/bcoin/output.js @@ -0,0 +1,60 @@ +/** + * output.js - output object for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * https://github.com/indutny/bcoin + */ + +var bn = require('bn.js'); +var bcoin = require('../bcoin'); +var utils = bcoin.utils; + +/** + * Output + */ + +function Output(options) { + var tx = options.tx; + var value; + + if (!(this instanceof Output)) + return new Output(options); + + if (!tx) + throw new Error('No TX passed into Input.'); + + value = options.value; + + if (typeof value === 'number' && (value | 0) === value) + value = new bn(value); + + this.value = utils.satoshi(value || new bn(0)); + this.script = options.script ? options.script.slice() : []; + + if (options.script && options.script._raw) + utils.hidden(this.script, '_raw', options.script._raw); +} + +Output.prototype.__defineGetter__('data', function() { + return bcoin.tx.getOutputData(this); +}); + +Output.prototype.__defineGetter__('addr', function() { + return this.data.addr; +}); + +Output.prototype.__defineGetter__('type', function() { + return this.data.type; +}); + +Output.prototype.__defineGetter__('lock', function() { + var lock = bcoin.script.lockTime(this.script); + if (!lock) + return; + return lock.toNumber(); +}); + +/** + * Expose + */ + +module.exports = Output; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 8124ecca..30011ceb 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -89,70 +89,51 @@ TX.prototype.input = function input(i, index) { return this; }; -TX.prototype._input = function _input(i, index) { - var hash, input, prev, lock, index, ex; +TX.prototype._input = function _input(obj, index) { + var options, hash, input, ex, i; - if (i instanceof TX) - i = { tx: i, index: index }; - else if (typeof i === 'string' || Array.isArray(i)) - i = { hash: i, index: index }; - - if (i.tx) - hash = i.tx.hash('hex'); - else if (i.out) - hash = i.out.hash; + if (obj instanceof TX) + options = { tx: obj, index: index }; + else if (typeof obj === 'string' || Array.isArray(obj)) + options = { hash: obj, index: index }; else - hash = i.hash; + options = obj; + + if (options.tx) + hash = options.tx.hash('hex'); + else if (options.out) + hash = options.out.hash; + else + hash = options.hash; if (typeof hash !== 'string') hash = utils.toHex(hash); - input = { + input = bcoin.input({ + tx: this, out: { - tx: (i.out ? i.out.tx : i.tx) || null, - hash: utils.toHex(hash), - index: i.out ? i.out.index : i.index + tx: options.out ? options.out.tx : options.tx, + hash: hash, + index: options.out ? options.out.index : options.index }, - script: i.script ? i.script.slice() : [], - seq: i.seq === undefined ? 0xffffffff : i.seq, - get data() { - return TX.getInputData(this); - } - }; - - if (i.script && i.script._raw) - utils.hidden(input.script, '_raw', i.script._raw); - - if (input.out.tx) { - prev = input.out.tx.outputs[input.out.index].script; - lock = bcoin.script.lockTime(prev); - if (lock) { - if (this._lock === 0) - this.lock = Math.max(lock.toNumber(), this.lock); - if (!bcoin.script.spendable(prev, this.lock)) - throw new Error('Cannot spend ' + utils.revHex(input.out.hash)); - } - } - - if (this.lock !== 0) { - if (i.seq === undefined) - input.seq = 0; - } + script: options.script, + seq: options.seq + }); // Try modifying existing input first - index = this._inputIndex(hash, index); - if (index !== -1) { - ex = this.inputs[index]; + i = this._inputIndex(hash, index); + if (i !== -1) { + ex = this.inputs[i]; input.out.tx = input.out.tx || ex.out.tx; input.seq = input.seq || ex.seq; input.script = input.script.length ? input.script : ex.script; - this.inputs[index] = input; + this.inputs[i] = input; } else { this.inputs.push(input); - index = this.inputs.length - 1; + i = this.inputs.length - 1; } - return index; + return i; }; TX.prototype._inputIndex = function _inputIndex(hash, index) { @@ -170,78 +151,6 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) { return -1; }; -TX.prototype.prevOut = function prevOut(i, def) { - var input; - - if (typeof i === 'object') - i = this.inputs.indexOf(i); - - input = this.inputs[i]; - - if (!input || !input.out.tx || input.out.index == null) - return def; - - return input.out.tx.outputs[input.out.index] || def; -}; - -TX.prototype.signatureHash = function signatureHash(i, s, type) { - var input, hash; - - if (s != null && !Array.isArray(s)) { - type = s; - s = null; - } - - if (typeof i === 'object') - i = this.inputs.indexOf(i); - - if (!type) - type = 'all'; - - if (typeof type === 'string') - type = constants.hashType[type]; - - // Get the current input. - input = this.inputs[i]; - - // Get the previous output's subscript - s = s || input.out.tx.getSubscript(input.out.index); - - // Get the hash of the current tx, minus the other inputs, plus the sighash. - hash = this.subscriptHash(i, s, type); - - return hash; -}; - -TX.prototype.signature = function signature(i, key, s, type) { - var hash, signature; - - if (s != null && !Array.isArray(s)) { - type = s; - s = null; - } - - if (typeof i === 'object') - i = this.inputs.indexOf(i); - - if (!type) - type = 'all'; - - if (typeof type === 'string') - type = constants.hashType[type]; - - // Get the hash of the current tx, minus the other inputs, plus the sighash. - hash = this.signatureHash(i, s, type); - - // Sign the transaction with our one input - signature = bcoin.ecdsa.sign(hash, key.priv).toDER(); - - // Add the sighash as a single byte to the signature - signature = signature.concat(type); - - return signature; -}; - // Build the scriptSigs for inputs, excluding the signatures TX.prototype.scriptInput = function scriptInput(input, pub) { // Get the previous output's subscript @@ -423,27 +332,26 @@ TX.prototype.scriptSig = function scriptSig(input, key, pub, type) { return input.script; }; -TX.prototype.output = function output(options, value) { - if (options instanceof bcoin.wallet) - options = options.getAddress(); +TX.prototype.output = function output(obj, value) { + var options; - if (typeof options === 'string') { + if (obj instanceof bcoin.wallet) + obj = obj.getAddress(); + + if (typeof obj === 'string') { options = { - address: options, + address: obj, value: value }; + } else { + options = obj; } - var output = { - value: new bn(options.value), - script: options.script ? options.script.slice() : [], - get data() { - return TX.getOutputData(this); - } - }; - - if (options.script && options.script._raw) - utils.hidden(output.script, '_raw', options.script._raw); + var output = bcoin.output({ + tx: this, + value: options.value, + script: options.script + }); this.outputs.push(output); @@ -458,13 +366,10 @@ TX.prototype.out = TX.prototype.output; TX.prototype.scriptOutput = function scriptOutput(output, options) { options = options || output; - var script = output.script ? output.script.slice() : []; + var script = output.script; var keys, m, n; var hash, color; - if (output.script && output.script._raw) - utils.hidden(script, '_raw', output.script._raw); - if (Array.isArray(options.keys || options.address)) { // Raw multisig transaction // https://github.com/bitcoin/bips/blob/master/bip-0010.mediawiki