diff --git a/.jshintrc b/.jshintrc index 1d70b696f..40fd8c6f6 100644 --- a/.jshintrc +++ b/.jshintrc @@ -28,7 +28,7 @@ "maxcomplexity": 6, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity) "maxdepth": 4, // Maximum depth of nested control structures "maxlen": 120, // Maximum number of cols in a line - "multistr": true // Allow use of multiline EOL escaping + "multistr": true, // Allow use of multiline EOL escaping "predef": [ // Extra globals. "after", diff --git a/lib/errors/spec.js b/lib/errors/spec.js index a3e42bcf5..70fbeacfb 100644 --- a/lib/errors/spec.js +++ b/lib/errors/spec.js @@ -61,6 +61,9 @@ module.exports = [{ }, { name: 'UnableToVerifySignature', message: format('Unable to verify signature: {0}') + }, { + name: 'DustOutputs', + message: format('Dust amount detected in one output') }, { name: 'FeeError', message: format('Fees are not correctly set {0}'), diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index a76f396a2..54ac4bc69 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -126,6 +126,9 @@ Transaction.prototype.checkedSerialize = Transaction.prototype.toString = functi throw new errors.Transaction.FeeError(feeError); } } + if (this._hasDustOutputs()) { + throw new errors.Transaction.DustOutputs(); + } return this.uncheckedSerialize(); }; @@ -143,6 +146,18 @@ Transaction.prototype._validateChange = function() { } }; +Transaction.DUST_AMOUNT = 5460; + +Transaction.prototype._hasDustOutputs = function() { + var output; + for (output in this.outputs) { + if (this.outputs[output].satoshis < Transaction.DUST_AMOUNT) { + return true; + } + } + return false; +}; + Transaction.prototype.inspect = function() { return ''; }; diff --git a/test/transaction/deserialize.js b/test/transaction/deserialize.js index 499a15196..28993d9b8 100644 --- a/test/transaction/deserialize.js +++ b/test/transaction/deserialize.js @@ -12,7 +12,7 @@ describe('Transaction deserialization', function() { vectors_valid.forEach(function(vector) { if (vector.length > 1) { var hexa = vector[1]; - Transaction(hexa).serialize().should.equal(hexa); + Transaction(hexa).serialize(true).should.equal(hexa); index++; } }); @@ -22,7 +22,7 @@ describe('Transaction deserialization', function() { vectors_invalid.forEach(function(vector) { if (vector.length > 1) { var hexa = vector[1]; - Transaction(hexa).serialize().should.equal(hexa); + Transaction(hexa).serialize(true).should.equal(hexa); index++; } }); diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 2a36acf3f..c42ae2469 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -242,6 +242,16 @@ describe('Transaction', function() { return transaction.serialize(); }).to.throw(errors.Transaction.FeeError); }); + it('fails if a dust transaction is created', function() { + var transaction = new Transaction() + .from(simpleUtxoWith1BTC) + .to(toAddress, 1) + .change(changeAddress) + .sign(privateKey); + expect(function() { + return transaction.serialize(); + }).to.throw(errors.Transaction.DustOutputs); + }); }); });