|
|
@ -108,14 +108,22 @@ Transaction.prototype._getHash = function() { |
|
|
|
* Retrieve a hexa string that can be used with bitcoind's CLI interface |
|
|
|
* (decoderawtransaction, sendrawtransaction) |
|
|
|
* |
|
|
|
* @param {boolean=} unsafe if true, skip testing for fees that are too high |
|
|
|
* @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: |
|
|
|
* <ul> |
|
|
|
* <li><tt>disableAll</tt>: disable all checks</li> |
|
|
|
* <li><tt>disableSmallFees</tt>: disable checking for fees that are too small</li> |
|
|
|
* <li><tt>disableLargeFees</tt>: disable checking for fees that are too large</li> |
|
|
|
* <li><tt>disableNotFullySigned</tt>: disable checking if all inputs are fully signed</li> |
|
|
|
* <li><tt>disableDustOutputs</tt>: disable checking if there are no outputs that are dust amounts</li> |
|
|
|
* </ul> |
|
|
|
* @return {string} |
|
|
|
*/ |
|
|
|
Transaction.prototype.serialize = function(unsafe) { |
|
|
|
if (unsafe) { |
|
|
|
if (true === unsafe || unsafe && unsafe.disableAll) { |
|
|
|
return this.uncheckedSerialize(); |
|
|
|
} else { |
|
|
|
return this.checkedSerialize(); |
|
|
|
return this.checkedSerialize(unsafe); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -123,29 +131,60 @@ Transaction.prototype.uncheckedSerialize = Transaction.prototype.toString = func |
|
|
|
return this.toBuffer().toString('hex'); |
|
|
|
}; |
|
|
|
|
|
|
|
Transaction.prototype.checkedSerialize = function() { |
|
|
|
var feeError = this._validateFees(); |
|
|
|
/** |
|
|
|
* Retrieve a hexa string that can be used with bitcoind's CLI interface |
|
|
|
* (decoderawtransaction, sendrawtransaction) |
|
|
|
* |
|
|
|
* @param {Object} skipOptions allows to skip certain tests: |
|
|
|
* <ul> |
|
|
|
* <li><tt>disableSmallFees</tt>: disable checking for fees that are too small</li> |
|
|
|
* <li><tt>disableLargeFees</tt>: disable checking for fees that are too large</li> |
|
|
|
* <li><tt>disableIsFullySigned</tt>: disable checking if all inputs are fully signed</li> |
|
|
|
* <li><tt>disableDustOutputs</tt>: disable checking if there are no outputs that are dust amounts</li> |
|
|
|
* </ul> |
|
|
|
* @return {string} |
|
|
|
*/ |
|
|
|
Transaction.prototype.checkedSerialize = function(skipOptions) { |
|
|
|
skipOptions = skipOptions || {}; |
|
|
|
var missingChange = this._missingChange(); |
|
|
|
if (feeError && missingChange) { |
|
|
|
throw new errors.Transaction.ChangeAddressMissing(); |
|
|
|
var feeIsTooLarge = this._isFeeTooLarge(); |
|
|
|
var feeIsTooSmall = this._isFeeTooSmall(); |
|
|
|
var isFullySigned = this.isFullySigned(); |
|
|
|
var hasDustOutputs = this._hasDustOutputs(); |
|
|
|
|
|
|
|
if (!skipOptions.disableLargeFees && feeIsTooLarge) { |
|
|
|
if (missingChange) { |
|
|
|
throw new errors.Transaction.ChangeAddressMissing('Fee is too large and no change address was provided'); |
|
|
|
} |
|
|
|
throw new errors.Transaction.FeeError(feeIsTooLarge); |
|
|
|
} |
|
|
|
if (feeError && !missingChange) { |
|
|
|
throw new errors.Transaction.FeeError(feeError); |
|
|
|
if (!skipOptions.disableSmallFees && feeIsTooSmall) { |
|
|
|
throw new errors.Transaction.FeeError(feeIsTooSmall); |
|
|
|
} |
|
|
|
if (this._hasDustOutputs()) { |
|
|
|
if (!skipOptions.disableDustOutputs && this._hasDustOutputs()) { |
|
|
|
throw new errors.Transaction.DustOutputs(); |
|
|
|
} |
|
|
|
if (!skipOptions.disableIsFullySigned && !isFullySigned) { |
|
|
|
throw new errors.Transaction.MissingSignatures(); |
|
|
|
} |
|
|
|
return this.uncheckedSerialize(); |
|
|
|
}; |
|
|
|
|
|
|
|
Transaction.FEE_SECURITY_MARGIN = 15; |
|
|
|
|
|
|
|
Transaction.prototype._validateFees = function() { |
|
|
|
if (this._getUnspentValue() > Transaction.FEE_SECURITY_MARGIN * this._estimateFee()) { |
|
|
|
return 'Fee is more than ' + Transaction.FEE_SECURITY_MARGIN + ' times the suggested amount'; |
|
|
|
Transaction.prototype._isFeeTooLarge = function() { |
|
|
|
var fee = this._getUnspentValue(); |
|
|
|
var maximumFee = Math.floor(Transaction.FEE_SECURITY_MARGIN * this._estimateFee()); |
|
|
|
if (fee > maximumFee) { |
|
|
|
return 'Fee is too large: expected less than ' + maximumFee + ' but got ' + fee; |
|
|
|
} |
|
|
|
if (this._getUnspentValue() < this._estimateFee() / Transaction.FEE_SECURITY_MARGIN) { |
|
|
|
return 'Fee is less than ' + Transaction.FEE_SECURITY_MARGIN + ' times the suggested amount'; |
|
|
|
}; |
|
|
|
|
|
|
|
Transaction.prototype._isFeeTooSmall = function() { |
|
|
|
var fee = this._getUnspentValue(); |
|
|
|
var minimumFee = Math.ceil(this._estimateFee() / Transaction.FEE_SECURITY_MARGIN); |
|
|
|
if (fee < minimumFee) { |
|
|
|
return 'Fee is too small: expected more than ' + minimumFee + ' but got ' + fee; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|