diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index 1f0b8ae..421f43f 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -240,6 +240,7 @@ Transaction.prototype.toObject = function toObject() { }); return { change: this._change ? this._change.toString() : undefined, + fee: this._fee ? this._fee : undefined, version: this.version, inputs: inputs, outputs: outputs, @@ -270,6 +271,9 @@ Transaction.prototype.fromObject = function(transaction) { if (transaction.change) { this.change(transaction.change); } + if (transaction.fee) { + this.fee(transaction.fee); + } this.nLockTime = transaction.nLockTime; this.version = transaction.version; }; @@ -354,8 +358,14 @@ Transaction.prototype.from = function(utxo, pubkeys, threshold) { }; Transaction.prototype._fromNonP2SH = function(utxo) { + var clazz; utxo = new UnspentOutput(utxo); - this.inputs.push(new PublicKeyHashInput({ + if (utxo.script.isPublicKeyHashOut()) { + clazz = PublicKeyHashInput; + } else { + clazz = Input; + } + this.addInput(new clazz({ output: new Output({ script: utxo.script, satoshis: utxo.satoshis @@ -365,7 +375,6 @@ Transaction.prototype._fromNonP2SH = function(utxo) { sequenceNumber: DEFAULT_SEQNUMBER, script: Script.empty() })); - this._inputAmount += utxo.satoshis; }; Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold) { @@ -394,10 +403,10 @@ Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold) { */ Transaction.prototype.addInput = function(input, outputScript, satoshis) { $.checkArgumentType(input, Input, 'input'); - if (!input.output || !(input.output instanceof Output) && !outputScript && !satoshis) { + if (!input.output && (_.isUndefined(outputScript) || _.isUndefined(satoshis))) { throw new errors.Transaction.NeedMoreInfo('Need information about the UTXO script and satoshis'); } - if (!input.output && outputScript && satoshis) { + if (!input.output && outputScript && !_.isUndefined(satoshis)) { outputScript = outputScript instanceof Script ? outputScript : new Script(outputScript); $.checkArgumentType(satoshis, 'number', 'satoshis'); input.output = new Output({ @@ -417,11 +426,11 @@ Transaction.prototype.addInput = function(input, outputScript, satoshis) { */ Transaction.prototype.uncheckedAddInput = function(input) { $.checkArgumentType(input, Input, 'input'); - this._changeSetup = false; this.inputs.push(input); if (input.output) { this._inputAmount += input.output.satoshis; } + this._updateChangeOutput(); return this; }; @@ -446,7 +455,7 @@ Transaction.prototype.hasAllUtxoInfo = function() { */ Transaction.prototype.fee = function(amount) { this._fee = amount; - this._changeSetup = false; + this._updateChangeOutput(); return this; }; @@ -463,7 +472,7 @@ Transaction.prototype.fee = function(amount) { */ Transaction.prototype.change = function(address) { this._change = new Address(address); - this._changeSetup = false; + this._updateChangeOutput(); return this; }; @@ -505,8 +514,12 @@ Transaction.prototype.addData = function(value) { Transaction.prototype.addOutput = function(output) { $.checkArgumentType(output, Output, 'output'); + this._addOutput(output); + this._updateChangeOutput(); +}; + +Transaction.prototype._addOutput = function(output) { this.outputs.push(output); - this._changeSetup = false; this._outputAmount += output.satoshis; }; @@ -514,12 +527,7 @@ Transaction.prototype._updateChangeOutput = function() { if (!this._change) { return; } - if (this._changeSetup) { - return; - } - if (!_.isUndefined(this._changeSetup)) { - this._clearSignatures(); - } + this._clearSignatures(); if (!_.isUndefined(this._changeOutput)) { this.removeOutput(this._changeOutput); } @@ -527,14 +535,13 @@ Transaction.prototype._updateChangeOutput = function() { var fee = this.getFee(); if (available - fee > 0) { this._changeOutput = this.outputs.length; - this.addOutput(new Output({ + this._addOutput(new Output({ script: Script.fromAddress(this._change), satoshis: available - fee })); } else { this._changeOutput = undefined; } - this._changeSetup = true; }; Transaction.prototype.getFee = function() { @@ -602,7 +609,6 @@ Transaction.prototype.removeOutput = function(index) { */ Transaction.prototype.sign = function(privateKey, sigtype) { $.checkState(this.hasAllUtxoInfo()); - this._updateChangeOutput(); var self = this; if (_.isArray(privateKey)) { _.each(privateKey, function(privateKey) { diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 096bf8f..c0f448c 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -250,6 +250,14 @@ describe('Transaction', function() { return transaction.serialize(true); }).to.not.throw(); }); + it('stores the fee set by the user', function() { + var fee = 1000000; + var serialized = new Transaction() + .fee(fee) + .toObject(); + var deserialized = new Transaction(serialized); + expect(deserialized._fee).to.equal(fee); + }); }); describe('checked serialize', function() { @@ -265,7 +273,8 @@ describe('Transaction', function() { var transaction = new Transaction() .from(simpleUtxoWith1BTC) .change(changeAddress) - .to(toAddress, 1); + .fee(50000000) + .to(toAddress, 40000000); expect(function() { return transaction.serialize(); }).to.throw(errors.Transaction.FeeError); @@ -325,6 +334,29 @@ describe('Transaction', function() { expect(deserialized.inputs[0] instanceof Transaction.Input.MultiSigScriptHash).to.equal(true); }); }); + + describe('checks on adding inputs', function() { + var transaction = new Transaction(); + it('fails if no output script is provided', function() { + expect(function() { + transaction.addInput(new Transaction.Input()); + }).to.throw(errors.Transaction.NeedMoreInfo); + }); + it('fails if no satoshi amount is provided', function() { + var input = new Transaction.Input(); + expect(function() { + transaction.addInput(input); + }).to.throw(errors.Transaction.NeedMoreInfo); + expect(function() { + transaction.addInput(new Transaction.Input(), Script.empty()); + }).to.throw(errors.Transaction.NeedMoreInfo); + }); + it('allows output and transaction to be feed as arguments', function() { + expect(function() { + transaction.addInput(new Transaction.Input(), Script.empty(), 0); + }).to.not.throw(); + }); + }); }); var tx_empty_hex = '01000000000000000000';