Browse Source

Merge pull request #1208 from braydonf/update-bn

Update bn.js to 2.0.4
patch-2
Manuel Aráoz 10 years ago
parent
commit
10cd5cf692
  1. 3
      lib/errors/spec.js
  2. 23
      lib/transaction/output.js
  3. 42
      lib/transaction/transaction.js
  4. 4
      lib/util/js.js
  5. 54
      npm-shrinkwrap.json
  6. 6
      package.json
  7. 4
      test/transaction/deserialize.js
  8. 8
      test/transaction/output.js
  9. 66
      test/transaction/transaction.js
  10. 23
      test/util/js.js

3
lib/errors/spec.js

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

23
lib/transaction/output.js

@ -9,6 +9,8 @@ var BufferWriter = require('../encoding/bufferwriter');
var Script = require('../script'); var Script = require('../script');
var $ = require('../util/preconditions'); var $ = require('../util/preconditions');
var MAX_SAFE_INTEGER = 0x1fffffffffffff;
function Output(params) { function Output(params) {
if (!(this instanceof Output)) { if (!(this instanceof Output)) {
return new Output(params); return new Output(params);
@ -48,16 +50,33 @@ Object.defineProperty(Output.prototype, 'satoshis', {
this._satoshis = parseInt(num); this._satoshis = parseInt(num);
this._satoshisBN = BN.fromNumber(this._satoshis); this._satoshisBN = BN.fromNumber(this._satoshis);
} else { } else {
$.checkArgument(
JSUtil.isNaturalNumber(num),
'Output satoshis is not a natural number'
);
this._satoshisBN = BN.fromNumber(num); this._satoshisBN = BN.fromNumber(num);
this._satoshis = num; this._satoshis = num;
} }
$.checkState( $.checkState(
JSUtil.isPositiveInteger(this._satoshis), JSUtil.isNaturalNumber(this._satoshis),
'Output satoshis is not a positive integer' 'Output satoshis is not a natural number'
); );
} }
}); });
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) { Output.prototype._fromObject = function(param) {
this.satoshis = param.satoshis; this.satoshis = param.satoshis;
if (param.script || param.scriptBuffer) { if (param.script || param.scriptBuffer) {

42
lib/transaction/transaction.js

@ -21,7 +21,6 @@ var MultiSigScriptHashInput = Input.MultiSigScriptHash;
var Output = require('./output'); var Output = require('./output');
var Script = require('../script'); var Script = require('../script');
var PrivateKey = require('../privatekey'); var PrivateKey = require('../privatekey');
var Block = require('../block');
var BN = require('../crypto/bn'); var BN = require('../crypto/bn');
/** /**
@ -60,6 +59,7 @@ function Transaction(serialized) {
var CURRENT_VERSION = 1; var CURRENT_VERSION = 1;
var DEFAULT_NLOCKTIME = 0; var DEFAULT_NLOCKTIME = 0;
var MAX_BLOCK_SIZE = 1000000;
// Minimum amount for an output for it not to be considered a dust output // Minimum amount for an output for it not to be considered a dust output
Transaction.DUST_AMOUNT = 546; Transaction.DUST_AMOUNT = 546;
@ -167,12 +167,22 @@ Transaction.prototype.checkedSerialize = function(opts) {
var serializationError = this.getSerializationError(opts); var serializationError = this.getSerializationError(opts);
if (serializationError) { if (serializationError) {
serializationError.message += ' Use Transaction#uncheckedSerialize if you want to skip security checks. ' + 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; throw serializationError;
} }
return this.uncheckedSerialize(); 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 * Retrieve a possible error that could appear when trying to serialize and broadcast this transaction
* *
@ -181,11 +191,15 @@ Transaction.prototype.checkedSerialize = function(opts) {
*/ */
Transaction.prototype.getSerializationError = function(opts) { Transaction.prototype.getSerializationError = function(opts) {
opts = opts || {}; opts = opts || {};
if (this.invalidSatoshis()) {
return new errors.Transaction.InvalidSatoshis();
}
var missingChange = this._missingChange(); var missingChange = this._missingChange();
var feeIsTooLarge = this._isFeeTooLarge(); var feeIsTooLarge = this._isFeeTooLarge();
var feeIsTooSmall = this._isFeeTooSmall(); var feeIsTooSmall = this._isFeeTooSmall();
var isFullySigned = this.isFullySigned(); var isFullySigned = this.isFullySigned();
var hasDustOutputs = this._hasDustOutputs();
if (!opts.disableLargeFees && feeIsTooLarge) { if (!opts.disableLargeFees && feeIsTooLarge) {
if (missingChange) { if (missingChange) {
@ -644,7 +658,7 @@ Transaction.prototype.getChangeOutput = function() {
*/ */
Transaction.prototype.to = function(address, amount) { Transaction.prototype.to = function(address, amount) {
$.checkArgument( $.checkArgument(
JSUtil.isPositiveInteger(amount), JSUtil.isNaturalNumber(amount),
'Amount is expected to be a positive integer' 'Amount is expected to be a positive integer'
); );
this.addOutput(new Output({ this.addOutput(new Output({
@ -981,28 +995,28 @@ Transaction.prototype.verify = function() {
return 'transaction txouts empty'; 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 // Check for negative or overflow output values
var valueoutbn = new BN(0); var valueoutbn = new BN(0);
for (var i = 0; i < this.outputs.length; i++) { for (var i = 0; i < this.outputs.length; i++) {
var txout = this.outputs[i]; var txout = this.outputs[i];
var valuebn = txout._satoshisBN;
if (valuebn.lt(BN.Zero)) { if (txout.invalidSatoshis()) {
return 'transaction txout ' + i + ' negative'; return 'transaction txout ' + i + ' satoshis is invalid';
} }
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'; 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))) { if (valueoutbn.gt(new BN(Transaction.MAX_MONEY))) {
return 'transaction txout ' + i + ' total output greater than 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 // Check for duplicate inputs
var txinmap = {}; var txinmap = {};
for (i = 0; i < this.inputs.length; i++) { for (i = 0; i < this.inputs.length; i++) {

4
lib/util/js.js

@ -70,12 +70,12 @@ module.exports = {
return target; return target;
}, },
/** /**
* Checks that a value is a positive integer * Checks that a value is a natural number, a positive integer or zero.
* *
* @param {*} value * @param {*} value
* @return {Boolean} * @return {Boolean}
*/ */
isPositiveInteger: function isPositiveInteger(value) { isNaturalNumber: function isNaturalNumber(value) {
return typeof value === 'number' && return typeof value === 'number' &&
isFinite(value) && isFinite(value) &&
Math.floor(value) === value && Math.floor(value) === value &&

54
npm-shrinkwrap.json

@ -1,58 +1,38 @@
{ {
"name": "bitcore", "name": "bitcore",
"version": "0.9.0", "version": "0.12.3",
"dependencies": { "dependencies": {
"bn.js": { "bn.js": {
"version": "0.16.1", "version": "2.0.4",
"from": "bn.js@0.16.1", "from": "bn.js@=2.0.4",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.16.1.tgz" "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-2.0.4.tgz"
}, },
"bs58": { "bs58": {
"version": "2.0.0", "version": "2.0.0",
"from": "bs58@2.0.0", "from": "bs58@=2.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.0.tgz" "resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.0.tgz"
}, },
"elliptic": { "elliptic": {
"version": "0.16.0", "version": "3.0.3",
"from": "elliptic@0.16.0", "from": "elliptic@=3.0.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-0.16.0.tgz", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-3.0.3.tgz",
"dependencies": { "dependencies": {
"bn.js": {
"version": "0.16.1",
"from": "bn.js@0.16.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.16.1.tgz"
},
"brorand": { "brorand": {
"version": "1.0.1", "version": "1.0.5",
"from": "brorand@1.0.1", "from": "brorand@^1.0.1",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.1.tgz" "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
},
"hash.js": {
"version": "0.3.2",
"from": "hash.js@0.3.2",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.3.2.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@^2.0.1"
} }
} }
}, },
"inherits": {
"version": "2.0.1",
"from": "inherits@2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"hash.js": { "hash.js": {
"version": "0.3.2", "version": "1.0.2",
"from": "hash.js@0.3.2", "from": "hash.js@^1.0.0",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.3.2.tgz", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.2.tgz"
"dependencies": { },
"inherits": { "inherits": {
"version": "2.0.1", "version": "2.0.1",
"from": "inherits@^2.0.1" "from": "inherits@=2.0.1",
} "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}, },
"lodash": { "lodash": {
"version": "2.4.1", "version": "2.4.1",

6
package.json

@ -79,10 +79,10 @@
"request": "browser-request" "request": "browser-request"
}, },
"dependencies": { "dependencies": {
"bn.js": "=0.16.1", "bn.js": "=2.0.4",
"bs58": "=2.0.0", "bs58": "=2.0.0",
"elliptic": "=0.16.0", "elliptic": "=3.0.3",
"hash.js": "=0.3.2", "hash.js": "=1.0.2",
"inherits": "=2.0.1", "inherits": "=2.0.1",
"lodash": "=2.4.1", "lodash": "=2.4.1",
"sha512": "=0.0.1" "sha512": "=0.0.1"

4
test/transaction/deserialize.js

@ -10,6 +10,7 @@ describe('Transaction deserialization', function() {
describe('valid transaction test case', function() { describe('valid transaction test case', function() {
var index = 0; var index = 0;
vectors_valid.forEach(function(vector) { vectors_valid.forEach(function(vector) {
it('vector #' + index, function() {
if (vector.length > 1) { if (vector.length > 1) {
var hexa = vector[1]; var hexa = vector[1];
Transaction(hexa).serialize(true).should.equal(hexa); Transaction(hexa).serialize(true).should.equal(hexa);
@ -17,9 +18,11 @@ describe('Transaction deserialization', function() {
} }
}); });
}); });
});
describe('invalid transaction test case', function() { describe('invalid transaction test case', function() {
var index = 0; var index = 0;
vectors_invalid.forEach(function(vector) { vectors_invalid.forEach(function(vector) {
it('invalid vector #' + index, function() {
if (vector.length > 1) { if (vector.length > 1) {
var hexa = vector[1]; var hexa = vector[1];
Transaction(hexa).serialize(true).should.equal(hexa); Transaction(hexa).serialize(true).should.equal(hexa);
@ -28,3 +31,4 @@ describe('Transaction deserialization', function() {
}); });
}); });
}); });
});

8
test/transaction/output.js

@ -45,7 +45,7 @@ describe('Output', function() {
satoshis: -100, satoshis: -100,
script: Script.empty() script: Script.empty()
}); });
}).should.throw('Output satoshis is not a positive integer'); }).should.throw('Output satoshis is not a natural number');
}); });
it('1.1', function() { it('1.1', function() {
@ -54,7 +54,7 @@ describe('Output', function() {
satoshis: 1.1, satoshis: 1.1,
script: Script.empty() script: Script.empty()
}); });
}).should.throw('Output satoshis is not a positive integer'); }).should.throw('Output satoshis is not a natural number');
}); });
it('NaN', function() { it('NaN', function() {
@ -63,7 +63,7 @@ describe('Output', function() {
satoshis: NaN, satoshis: NaN,
script: Script.empty() script: Script.empty()
}); });
}).should.throw('Output satoshis is not a positive integer'); }).should.throw('Output satoshis is not a natural number');
}); });
it('Infinity', function() { it('Infinity', function() {
@ -72,7 +72,7 @@ describe('Output', function() {
satoshis: Infinity, satoshis: Infinity,
script: Script.empty() script: Script.empty()
}); });
}).should.throw('Output satoshis is not a positive integer'); }).should.throw('Output satoshis is not a natural number');
}); });
}); });

66
test/transaction/transaction.js

@ -8,6 +8,7 @@ var _ = require('lodash');
var sinon = require('sinon'); var sinon = require('sinon');
var bitcore = require('../..'); var bitcore = require('../..');
var BN = bitcore.crypto.BN;
var Transaction = bitcore.Transaction; var Transaction = bitcore.Transaction;
var PrivateKey = bitcore.PrivateKey; var PrivateKey = bitcore.PrivateKey;
var Script = bitcore.Script; var Script = bitcore.Script;
@ -245,6 +246,18 @@ describe('Transaction', function() {
transaction.outputs.length.should.equal(2); transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.equal(10000); 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() { it('if fee is too small, fail serialization', function() {
var transaction = new Transaction() var transaction = new Transaction()
.from(simpleUtxoWith100000Satoshis) .from(simpleUtxoWith100000Satoshis)
@ -355,7 +368,7 @@ describe('Transaction', function() {
var buildSkipTest = function(builder, check) { var buildSkipTest = function(builder, check) {
return function() { return function() {
var transaction = new Transaction(); var transaction = new Transaction();
transaction.from(simpleUtxoWith1BTC) transaction.from(simpleUtxoWith1BTC);
builder(transaction); builder(transaction);
var options = {}; var options = {};
@ -410,6 +423,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 is invalid');
});
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 satoshis is invalid');
});
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() { describe('to and from JSON', function() {
it('takes a string that is a valid JSON and deserializes from it', function() { it('takes a string that is a valid JSON and deserializes from it', function() {
var simple = new Transaction(); var simple = new Transaction();

23
test/util/js.js

@ -32,44 +32,49 @@ describe('js utils', function() {
}); });
describe('isPositiveInteger', function() { describe('isNaturalNumber', function() {
it('false for float', function() { it('false for float', function() {
var a = JSUtil.isPositiveInteger(0.1); var a = JSUtil.isNaturalNumber(0.1);
a.should.equal(false); a.should.equal(false);
}); });
it('false for string float', function() { it('false for string float', function() {
var a = JSUtil.isPositiveInteger('0.1'); var a = JSUtil.isNaturalNumber('0.1');
a.should.equal(false); a.should.equal(false);
}); });
it('false for string integer', function() { it('false for string integer', function() {
var a = JSUtil.isPositiveInteger('1'); var a = JSUtil.isNaturalNumber('1');
a.should.equal(false); a.should.equal(false);
}); });
it('false for negative integer', function() { it('false for negative integer', function() {
var a = JSUtil.isPositiveInteger(-1); var a = JSUtil.isNaturalNumber(-1);
a.should.equal(false); a.should.equal(false);
}); });
it('false for negative integer string', function() { it('false for negative integer string', function() {
var a = JSUtil.isPositiveInteger('-1'); var a = JSUtil.isNaturalNumber('-1');
a.should.equal(false); a.should.equal(false);
}); });
it('false for infinity', function() { it('false for infinity', function() {
var a = JSUtil.isPositiveInteger(Infinity); var a = JSUtil.isNaturalNumber(Infinity);
a.should.equal(false); a.should.equal(false);
}); });
it('false for NaN', function() { it('false for NaN', function() {
var a = JSUtil.isPositiveInteger(NaN); var a = JSUtil.isNaturalNumber(NaN);
a.should.equal(false); a.should.equal(false);
}); });
it('true for zero', function() {
var a = JSUtil.isNaturalNumber(0);
a.should.equal(true);
});
it('true for positive integer', function() { it('true for positive integer', function() {
var a = JSUtil.isPositiveInteger(1000); var a = JSUtil.isNaturalNumber(1000);
a.should.equal(true); a.should.equal(true);
}); });

Loading…
Cancel
Save