From c5d7eacfacddf1745aa388c2b230ec8061dde6dd Mon Sep 17 00:00:00 2001 From: eordano Date: Tue, 24 Feb 2015 15:50:28 -0300 Subject: [PATCH] Add check for output amount > input amount --- lib/errors/spec.js | 3 ++ lib/transaction/transaction.js | 57 ++++++++++++++++++++++----------- test/transaction/transaction.js | 7 ++++ 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/lib/errors/spec.js b/lib/errors/spec.js index 6448913..95751cf 100644 --- a/lib/errors/spec.js +++ b/lib/errors/spec.js @@ -60,6 +60,9 @@ module.exports = [{ }, { name: 'NeedMoreInfo', message: '{0}' + }, { + name: 'InvalidOutputAmountSum', + message: '{0}' }, { name: 'MissingSignatures', message: 'Some inputs have not been fully signed' diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index c5e4500..cb9d770 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -110,13 +110,12 @@ Transaction.prototype._getHash = function() { * * @param {Object|boolean=} unsafe if true, skip all tests. if it's an object, * it's expected to contain a set of flags to skip certain tests: - * + * * `disableAll`: disable all checks + * * `disableSmallFees`: disable checking for fees that are too small + * * `disableLargeFees`: disable checking for fees that are too large + * * `disableNotFullySigned`: disable checking if all inputs are fully signed + * * `disableDustOutputs`: disable checking if there are no outputs that are dust amounts + * * `disableMoreOutputThanInput`: disable checking if the transaction spends more bitcoins than the sum of the input amounts * @return {string} */ Transaction.prototype.serialize = function(unsafe) { @@ -136,15 +135,33 @@ Transaction.prototype.uncheckedSerialize = Transaction.prototype.toString = func * (decoderawtransaction, sendrawtransaction) * * @param {Object} opts allows to skip certain tests: - * + * * `disableSmallFees`: disable checking for fees that are too small + * * `disableLargeFees`: disable checking for fees that are too large + * * `disableIsFullySigned`: disable checking if all inputs are fully signed + * * `disableDustOutputs`: disable checking if there are no outputs that are dust amounts + * * `disableMoreOutputThanInput`: disable checking if the transaction spends more bitcoins than the sum of the input amounts * @return {string} */ Transaction.prototype.checkedSerialize = function(opts) { + var serializationError = this.getSerializationError(opts); + if (serializationError) { + throw serializationError; + } + return this.uncheckedSerialize(); +}; + +/** + * Retrieve a possible error that could appear when trying to serialize and broadcast this transaction + * + * @param {Object} opts allows to skip certain tests: + * * `disableSmallFees`: disable checking for fees that are too small + * * `disableLargeFees`: disable checking for fees that are too large + * * `disableIsFullySigned`: disable checking if all inputs are fully signed + * * `disableDustOutputs`: disable checking if there are no outputs that are dust amounts + * * `disableMoreOutputThanInput`: disable checking if the transaction spends more bitcoins than the sum of the input amounts + * @return {bitcore.Error} + */ +Transaction.prototype.getSerializationError = function(opts) { opts = opts || {}; var missingChange = this._missingChange(); var feeIsTooLarge = this._isFeeTooLarge(); @@ -154,20 +171,22 @@ Transaction.prototype.checkedSerialize = function(opts) { if (!opts.disableLargeFees && feeIsTooLarge) { if (missingChange) { - throw new errors.Transaction.ChangeAddressMissing('Fee is too large and no change address was provided'); + return new errors.Transaction.ChangeAddressMissing('Fee is too large and no change address was provided'); } - throw new errors.Transaction.FeeError(feeIsTooLarge); + return new errors.Transaction.FeeError(feeIsTooLarge); } if (!opts.disableSmallFees && feeIsTooSmall) { - throw new errors.Transaction.FeeError(feeIsTooSmall); + return new errors.Transaction.FeeError(feeIsTooSmall); } if (!opts.disableDustOutputs && this._hasDustOutputs()) { - throw new errors.Transaction.DustOutputs(); + return new errors.Transaction.DustOutputs(); } if (!opts.disableIsFullySigned && !isFullySigned) { - throw new errors.Transaction.MissingSignatures(); + return new errors.Transaction.MissingSignatures(); + } + if (!opts.disableMoreOutputThanInput && this._getUnspentValue < 0) { + return new errors.Transaction.InvalidOutputAmountSum(); } - return this.uncheckedSerialize(); }; Transaction.FEE_SECURITY_MARGIN = 15; diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index bd97e4b..c1195bb 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -388,6 +388,13 @@ describe('Transaction', function() { .change(changeAddress); }, 'disableIsFullySigned'); }); + it('can skip the check that avoids spending more bitcoins than the inputs for a transaction', function() { + buildSkipTest(function(transaction) { + return transaction + .to(toAddress, 10000000000) + .change(changeAddress); + }, 'disableMoreOutputThanInput'); + }); }); });