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
- *
+ * * `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
- *
+ * * `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');
+ });
});
});