diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index d75d17c..ac8acdb 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -21,9 +21,10 @@ 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 MAX_BLOCK_SIZE = 1000000; + /** * Represents a transaction, a set of inputs and outputs to change ownership of tokens * @@ -186,7 +187,6 @@ Transaction.prototype.getSerializationError = function(opts) { var feeIsTooLarge = this._isFeeTooLarge(); var feeIsTooSmall = this._isFeeTooSmall(); var isFullySigned = this.isFullySigned(); - var hasDustOutputs = this._hasDustOutputs(); if (!opts.disableLargeFees && feeIsTooLarge) { if (missingChange) { @@ -982,37 +982,33 @@ Transaction.prototype.verify = function() { 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 = new BN(0); for (var i = 0; i < this.outputs.length; i++) { var txout = this.outputs[i]; - if (txout._satoshis > MAX_SAFE_INTEGER) { return 'transaction txout ' + i + ' satoshis greater than max safe integer'; } - if (txout._satoshis !== txout._satoshisBN.toNumber()) { return 'transaction txout ' + i + ' satoshis has corrupted value'; } - - var valuebn = new BN(txout._satoshis, 10); - if (valuebn.lt(BN.Zero)) { + if (txout._satoshis < 0) { return 'transaction txout ' + i + ' negative'; } - if (valuebn.gt(new BN(Transaction.MAX_MONEY, 10))) { + if (txout._satoshisBN.gt(new BN(Transaction.MAX_MONEY, 10))) { return 'transaction txout ' + i + ' greater than MAX_MONEY'; } - valueoutbn = valueoutbn.add(valuebn); + valueoutbn = valueoutbn.add(txout._satoshisBN); if (valueoutbn.gt(new BN(Transaction.MAX_MONEY))) { return 'transaction txout ' + i + ' total output greater than MAX_MONEY'; } } + // Size limits + if (this.toBuffer().length > MAX_BLOCK_SIZE) { + return 'transaction over the maximum block size'; + } + // Check for duplicate inputs var txinmap = {}; for (i = 0; i < this.inputs.length; i++) { diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 2651038..27f8b5f 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -8,6 +8,7 @@ var _ = require('lodash'); var sinon = require('sinon'); var bitcore = require('../..'); +var BN = bitcore.crypto.BN; var Transaction = bitcore.Transaction; var PrivateKey = bitcore.PrivateKey; var Script = bitcore.Script; @@ -355,7 +356,7 @@ describe('Transaction', function() { var buildSkipTest = function(builder, check) { return function() { var transaction = new Transaction(); - transaction.from(simpleUtxoWith1BTC) + transaction.from(simpleUtxoWith1BTC); builder(transaction); var options = {}; @@ -410,6 +411,57 @@ describe('Transaction', function() { }); }); + describe('#verify', function() { + + it('not if _satoshis and _satoshisBN have different values', function() { + var tx = new Transaction() + .from({ + 'txId': testPrevTx, + 'outputIndex': 0, + 'script': testScript, + 'satoshis': testAmount + }).to('mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc', testAmount - 10000); + + tx.outputs[0]._satoshis = 100; + tx.outputs[0]._satoshisBN = new BN('fffffffffffffff', 16); + var verify = tx.verify(); + verify.should.equal('transaction txout 0 satoshis has corrupted value'); + }); + + it('not if _satoshis is negative', function() { + var tx = new Transaction() + .from({ + 'txId': testPrevTx, + 'outputIndex': 0, + 'script': testScript, + 'satoshis': testAmount + }).to('mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc', testAmount - 10000); + + tx.outputs[0]._satoshis = -100; + tx.outputs[0]._satoshisBN = new BN(-100, 10); + var verify = tx.verify(); + verify.should.equal('transaction txout 0 negative'); + }); + + it('not if transaction is greater than max block size', function() { + + var tx = new Transaction() + .from({ + 'txId': testPrevTx, + 'outputIndex': 0, + 'script': testScript, + 'satoshis': testAmount + }).to('mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc', testAmount - 10000); + + tx.toBuffer = sinon.stub().returns({length: 10000000}); + + var verify = tx.verify(); + verify.should.equal('transaction over the maximum block size'); + + }); + + }); + describe('to and from JSON', function() { it('takes a string that is a valid JSON and deserializes from it', function() { var simple = new Transaction(); @@ -616,7 +668,7 @@ describe('Transaction', function() { transaction.outputAmount.should.equal(99990000); }); it('returns correct values for coinjoin transaction', function() { - // see livenet tx c16467eea05f1f30d50ed6dbc06a38539d9bb15110e4b7dc6653046a3678a718 + // see livenet tx c16467eea05f1f30d50ed6dbc06a38539d9bb15110e4b7dc6653046a3678a718 var transaction = new Transaction(txCoinJoinHex); transaction.outputAmount.should.equal(4191290961); expect(function() {