Included satoshis check during checked serialization.

This commit is contained in:
Braydon Fuller 2015-04-30 13:14:19 -04:00
parent cd12164fae
commit f8ea7e39cc
4 changed files with 52 additions and 14 deletions

View File

@ -81,6 +81,9 @@ module.exports = [{
}, {
name: 'DustOutputs',
message: 'Dust amount detected in one output'
}, {
name: 'InvalidSatoshis',
message: 'Output satoshis are invalid',
}, {
name: 'FeeError',
message: 'Fees are not correctly set {0}',

View File

@ -9,6 +9,8 @@ var BufferWriter = require('../encoding/bufferwriter');
var Script = require('../script');
var $ = require('../util/preconditions');
var MAX_SAFE_INTEGER = 0x1fffffffffffff;
function Output(params) {
if (!(this instanceof Output)) {
return new Output(params);
@ -62,6 +64,19 @@ Object.defineProperty(Output.prototype, 'satoshis', {
}
});
Output.prototype.invalidSatoshis = function() {
if (this._satoshis > MAX_SAFE_INTEGER) {
return 'transaction txout satoshis greater than max safe integer';
}
if (this._satoshis !== this._satoshisBN.toNumber()) {
return 'transaction txout satoshis has corrupted value';
}
if (this._satoshis < 0) {
return 'transaction txout negative';
}
return false;
};
Output.prototype._fromObject = function(param) {
this.satoshis = param.satoshis;
if (param.script || param.scriptBuffer) {

View File

@ -23,8 +23,6 @@ var Script = require('../script');
var PrivateKey = require('../privatekey');
var BN = require('../crypto/bn');
var MAX_BLOCK_SIZE = 1000000;
/**
* Represents a transaction, a set of inputs and outputs to change ownership of tokens
*
@ -61,7 +59,7 @@ function Transaction(serialized) {
var CURRENT_VERSION = 1;
var DEFAULT_NLOCKTIME = 0;
var MAX_SAFE_INTEGER = 0x1fffffffffffff;
var MAX_BLOCK_SIZE = 1000000;
// Minimum amount for an output for it not to be considered a dust output
Transaction.DUST_AMOUNT = 546;
@ -169,12 +167,22 @@ Transaction.prototype.checkedSerialize = function(opts) {
var serializationError = this.getSerializationError(opts);
if (serializationError) {
serializationError.message += ' Use Transaction#uncheckedSerialize if you want to skip security checks. ' +
'See http://bitcore.io/guide/transaction.html#Serialization for more info.'
'See http://bitcore.io/guide/transaction.html#Serialization for more info.';
throw serializationError;
}
return this.uncheckedSerialize();
};
Transaction.prototype.invalidSatoshis = function() {
var invalid = false;
for (var i = 0; i < this.outputs.length; i++) {
if (this.outputs[i].invalidSatoshis()) {
invalid = true;
}
}
return invalid;
};
/**
* Retrieve a possible error that could appear when trying to serialize and broadcast this transaction
*
@ -183,6 +191,11 @@ Transaction.prototype.checkedSerialize = function(opts) {
*/
Transaction.prototype.getSerializationError = function(opts) {
opts = opts || {};
if (this.invalidSatoshis()) {
return new errors.Transaction.InvalidSatoshis();
}
var missingChange = this._missingChange();
var feeIsTooLarge = this._isFeeTooLarge();
var feeIsTooSmall = this._isFeeTooSmall();
@ -986,14 +999,9 @@ Transaction.prototype.verify = function() {
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';
}
if (txout._satoshis < 0) {
return 'transaction txout ' + i + ' negative';
if (txout.invalidSatoshis()) {
return 'transaction txout ' + i + ' satoshis is invalid';
}
if (txout._satoshisBN.gt(new BN(Transaction.MAX_MONEY, 10))) {
return 'transaction txout ' + i + ' greater than MAX_MONEY';

View File

@ -246,6 +246,18 @@ describe('Transaction', function() {
transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.equal(10000);
});
it('if satoshis are invalid', function() {
var transaction = new Transaction()
.from(simpleUtxoWith100000Satoshis)
.to(toAddress, 99999)
.change(changeAddress)
.sign(privateKey);
transaction.outputs[0]._satoshis = 100;
transaction.outputs[0]._satoshisBN = new BN(101, 10);
expect(function() {
return transaction.serialize();
}).to.throw(errors.Transaction.InvalidSatoshis);
});
it('if fee is too small, fail serialization', function() {
var transaction = new Transaction()
.from(simpleUtxoWith100000Satoshis)
@ -425,7 +437,7 @@ describe('Transaction', function() {
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');
verify.should.equal('transaction txout 0 satoshis is invalid');
});
it('not if _satoshis is negative', function() {
@ -440,7 +452,7 @@ describe('Transaction', function() {
tx.outputs[0]._satoshis = -100;
tx.outputs[0]._satoshisBN = new BN(-100, 10);
var verify = tx.verify();
verify.should.equal('transaction txout 0 negative');
verify.should.equal('transaction txout 0 satoshis is invalid');
});
it('not if transaction is greater than max block size', function() {