|
|
@ -22,6 +22,8 @@ var MultiSigScriptHashInput = Input.MultiSigScriptHash; |
|
|
|
var Output = require('./output'); |
|
|
|
var Script = require('../script'); |
|
|
|
var PrivateKey = require('../privatekey'); |
|
|
|
var Block = require('../block'); |
|
|
|
var BN = require('../crypto/bn'); |
|
|
|
|
|
|
|
var CURRENT_VERSION = 1; |
|
|
|
var DEFAULT_NLOCKTIME = 0; |
|
|
@ -57,10 +59,13 @@ function Transaction(serialized) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// max amount of satoshis in circulation
|
|
|
|
Transaction.MAX_MONEY = 21000000 * 1e8; |
|
|
|
|
|
|
|
/* Constructors and Serialization */ |
|
|
|
|
|
|
|
/** |
|
|
|
* Create a "shallow" copy of the transaction, by serializing and deserializing |
|
|
|
* Create a 'shallow' copy of the transaction, by serializing and deserializing |
|
|
|
* it dropping any additional information that inputs and outputs may have hold |
|
|
|
* |
|
|
|
* @param {Transaction} transaction |
|
|
@ -562,8 +567,82 @@ Transaction.prototype.isValidSignature = function(signature) { |
|
|
|
/** |
|
|
|
* @returns {bool} whether the signature is valid for this transaction input |
|
|
|
*/ |
|
|
|
Transaction.prototype.verify = function(sig, pubkey, nin, subscript) { |
|
|
|
Transaction.prototype.verifySignature = function(sig, pubkey, nin, subscript) { |
|
|
|
return Sighash.verify(this, sig, pubkey, nin, subscript); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Check that a transaction passes basic sanity tests. If not, return a string |
|
|
|
* describing the error. This function contains the same logic as |
|
|
|
* CheckTransaction in bitcoin core. |
|
|
|
*/ |
|
|
|
Transaction.prototype.verify = function() { |
|
|
|
// Basic checks that don't depend on any context
|
|
|
|
if (this.inputs.length === 0) { |
|
|
|
return 'transaction txins empty'; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.outputs.length === 0) { |
|
|
|
return 'transaction txouts empty'; |
|
|
|
} |
|
|
|
|
|
|
|
// Size limits
|
|
|
|
if (this.toBuffer().length > Block.MAX_BLOCK_SIZE) { |
|
|
|
return 'transaction over the maximum block size'; |
|
|
|
} |
|
|
|
|
|
|
|
// Check for negative or overflow output values
|
|
|
|
var valueoutbn = BN(0); |
|
|
|
for (var i = 0; i < this.outputs.length; i++) { |
|
|
|
var txout = this.outputs[i]; |
|
|
|
var valuebn = BN(txout.satoshis.toString(16)); |
|
|
|
if (valuebn.lt(0)) { |
|
|
|
return 'transaction txout ' + i + ' negative'; |
|
|
|
} |
|
|
|
if (valuebn.gt(Transaction.MAX_MONEY)) { |
|
|
|
return 'transaction txout ' + i + ' greater than MAX_MONEY'; |
|
|
|
} |
|
|
|
valueoutbn = valueoutbn.add(valuebn); |
|
|
|
if (valueoutbn.gt(Transaction.MAX_MONEY)) { |
|
|
|
return 'transaction txout ' + i + ' total output greater than MAX_MONEY'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Check for duplicate inputs
|
|
|
|
var txinmap = {}; |
|
|
|
for (i = 0; i < this.inputs.length; i++) { |
|
|
|
var txin = this.inputs[i]; |
|
|
|
|
|
|
|
var inputid = txin.prevTxId + ':' + txin.outputIndex; |
|
|
|
if (!_.isUndefined(txinmap[inputid])) { |
|
|
|
return 'transaction input ' + i + ' duplicate input'; |
|
|
|
} |
|
|
|
txinmap[inputid] = true; |
|
|
|
} |
|
|
|
|
|
|
|
var isCoinbase = this.isCoinbase(); |
|
|
|
if (isCoinbase) { |
|
|
|
var buf = this.inputs[0]._script.toBuffer(); |
|
|
|
if (buf.length < 2 || buf.length > 100) { |
|
|
|
return 'coinbase trasaction script size invalid'; |
|
|
|
} |
|
|
|
} else { |
|
|
|
for (i = 0; i < this.inputs.length; i++) { |
|
|
|
if (this.inputs[i].isNull()) { |
|
|
|
return 'tranasction input ' + i + ' has null input'; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Analagous to bitcoind's IsCoinBase function in transaction.h |
|
|
|
*/ |
|
|
|
Transaction.prototype.isCoinbase = function() { |
|
|
|
return (this.inputs.length === 1 && this.inputs[0].isNull()); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
module.exports = Transaction; |
|
|
|