more tests. utils.

This commit is contained in:
Christopher Jeffrey 2016-05-15 03:33:58 -07:00
parent 022de4a91d
commit 4b0519a073
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 333 additions and 54 deletions

View File

@ -431,10 +431,26 @@ Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
Block.prototype.getReward = function getReward(network) {
var reward = Block.reward(this.height, network);
var i;
var i, fee;
for (i = 1; i < this.txs.length; i++)
reward += this.txs[i].getFee();
for (i = 1; i < this.txs.length; i++) {
fee = this.txs[i].getFee();
if (fee < 0 || fee > constants.MAX_MONEY)
return -1;
reward += fee;
// We don't want to go above 53 bits.
// This is to make the getClaimed check
// fail if the miner mined an evil block.
// Note that this check ONLY works because
// MAX_MONEY is 51 bits. The result of
// (51 bits + 51 bits) is _never_ greater
// than 52 bits.
if (reward < 0 || reward > constants.MAX_MONEY)
return -1;
}
return reward;
};
@ -459,6 +475,8 @@ Block.prototype.getClaimed = function getClaimed() {
Block.reward = function _reward(height, network) {
var halvings, reward;
assert(height !== -1, 'Height is negative.');
network = bcoin.network.get(network);
halvings = height / network.halvingInterval | 0;

View File

@ -633,23 +633,16 @@ utils.assert.fatal = function fatal(value, message) {
};
/**
* One bitcoin in satoshis.
* @const {BN}
* @default
*/
utils.COIN = 100000000;
/**
* Convert satoshis to a BTC string. Note that
* BTC strings _always_ have a decimal point.
* @param {BN|Number} satoshi
* Convert satoshis to a BTC string.
* This function explicitly avoids
* any floating point arithmetic.
* @param {Number} satoshi
* @returns {String} BTC string.
*/
utils.btc = function btc(satoshi) {
var neg = false;
var btc, hi, lo;
var negative = false;
var btc;
if (utils.isBTC(satoshi))
return satoshi;
@ -658,24 +651,22 @@ utils.btc = function btc(satoshi) {
if (satoshi < 0) {
satoshi = -satoshi;
neg = true;
negative = true;
}
hi = Math.floor(satoshi / utils.COIN).toString(10);
lo = (satoshi % utils.COIN).toString(10);
btc = satoshi.toString(10);
while (lo.length < 8)
lo = '0' + lo;
while (btc.length < 9)
btc = '0' + btc;
lo = '.' + lo;
btc = btc.slice(0, -8) + '.' + btc.slice(-8);
lo = lo.replace(/0+$/, '');
if (lo === '.')
lo += '0';
btc = btc.replace(/0+$/, '');
btc = hi + lo;
if (btc[btc.length - 1] === '.')
btc = btc.slice(0, -1);
if (neg)
if (negative)
btc = '-' + btc;
return btc;
@ -683,12 +674,15 @@ utils.btc = function btc(satoshi) {
/**
* Convert BTC string to satoshis.
* @param {String|Number} btc
* @returns {BN} Satoshis.
* This function explicitly avoids
* any floating point arithmetic.
* @param {String} btc
* @returns {Number} Satoshis.
* @throws on parse error
*/
utils.satoshi = function satoshi(btc) {
var neg = false;
var negative = false;
var satoshi, parts, hi, lo;
if (utils.isSatoshi(btc))
@ -697,24 +691,31 @@ utils.satoshi = function satoshi(btc) {
assert(utils.isBTC(btc), 'Non-BTC value for conversion.');
if (btc[0] === '-') {
neg = true;
negative = true;
btc = btc.substring(1);
}
parts = btc.split('.');
assert(parts.length <= 2, 'Bad decimal point.');
hi = parts[0] || '0';
lo = parts[1] || '0';
hi = hi.replace(/^0+/, '');
lo = lo.replace(/0+$/, '');
assert(hi.length <= 8, 'Number exceeds MAX_MONEY.');
assert(+hi < 21000000, 'Number exceeds MAX_MONEY.');
assert(lo.length <= 8, 'Too many decimal places.');
while (lo.length < 8)
lo += '0';
assert(lo.length === 8);
satoshi = parseInt(hi + lo, 10);
satoshi = (hi + lo).replace(/^0+/, '');
satoshi = parseInt(satoshi, 10);
if (neg)
if (negative)
satoshi = -satoshi;
return satoshi;
@ -747,7 +748,10 @@ utils.isSatoshi = function isSatoshi(val) {
*/
utils.isBTC = function isBTC(val) {
return typeof val === 'string' && /^-?\d+\.\d+$/.test(val);
return typeof val === 'string'
&& /^-?(\d+)?(?:\.\d*)?$/.test(val)
&& val.length !== 0
&& val !== '-';
};
/**
@ -1083,7 +1087,7 @@ utils.uniq = function uniq(obj) {
};
/**
* Convert a mantissa/compact number to a big number.
* Convert a compact number to a big number.
* Used for `block.bits` -> `target` conversion.
* @param {Number} compact
* @returns {BN}
@ -1115,7 +1119,7 @@ utils.fromCompact = function fromCompact(compact) {
};
/**
* Convert a big number to a mantissa/compact number.
* Convert a big number to a compact number.
* Used for `target` -> `block.bits` conversion.
* @param {BN} num
* @returns {Number}
@ -1148,8 +1152,7 @@ utils.toCompact = function toCompact(num) {
if (num.isNeg())
compact |= 0x800000;
if (compact < 0)
compact += 0x100000000;
compact >>>= 0;
return compact;
};
@ -1589,7 +1592,7 @@ utils.MAX_SAFE_INTEGER = 0x1fffffffffffff;
*/
utils.write64N = function write64N(dst, num, off, be) {
var neg, hi, lo;
var negative, hi, lo;
assert(typeof num === 'number');
@ -1597,18 +1600,17 @@ utils.write64N = function write64N(dst, num, off, be) {
assert(num <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1');
if (num < 0)
neg = true;
negative = num < 0;
num = num < 0 ? -num : num;
if (neg)
num--;
if (negative) {
num = -num;
num -= 1;
}
hi = num / 0x100000000 | 0;
lo = num % 0x100000000;
if (neg) {
if (negative) {
hi = ~hi >>> 0;
lo = ~lo >>> 0;
}

View File

@ -52,8 +52,24 @@ describe('Block', function() {
it('should be jsonified and unjsonified and still verify', function() {
var json = block.toRaw();
var b = bcoin.merkleblock.fromRaw(json);
// FIXME
//assert.equal(b.render(), json);
assert.deepEqual(b.render(), json);
assert(b.verify());
});
it('should calculate reward properly', function() {
var height = 0;
var total = 0;
var reward;
for (;;) {
reward = bcoin.block.reward(height);
total += reward;
if (reward === 0)
break;
height++;
}
assert.equal(height, 6930000);
assert.equal(total, 2099999997690000);
});
});

View File

@ -269,4 +269,178 @@ describe('TX', function() {
});
});
});
function createInput(value) {
var hash = bcoin.ec.random(32).toString('hex');
return {
prevout: {
hash: hash,
index: 0
},
coin: {
version: 1,
height: 0,
value: value,
script: [],
coinbase: false,
hash: hash,
index: 0
},
script: [],
witness: [],
sequence: 0xffffffff
};
}
it('should fail on >51 bit coin values', function () {
var tx = bcoin.tx({
version: 1,
inputs: [createInput(constants.MAX_MONEY + 1)],
outputs: [{
script: [],
value: constants.MAX_MONEY
}],
locktime: 0
});
assert.ok(tx.isSane());
assert.ok(!tx.checkInputs(0));
});
it('should handle 51 bit coin values', function () {
var tx = bcoin.tx({
version: 1,
inputs: [createInput(constants.MAX_MONEY)],
outputs: [{
script: [],
value: constants.MAX_MONEY
}],
locktime: 0
});
assert.ok(tx.isSane());
assert.ok(tx.checkInputs(0));
});
it('should fail on >51 bit output values', function () {
var tx = bcoin.tx({
version: 1,
inputs: [createInput(constants.MAX_MONEY)],
outputs: [{
script: [],
value: constants.MAX_MONEY + 1
}],
locktime: 0
});
assert.ok(!tx.isSane());
assert.ok(!tx.checkInputs(0));
});
it('should handle 51 bit output values', function () {
var tx = bcoin.tx({
version: 1,
inputs: [createInput(constants.MAX_MONEY)],
outputs: [{
script: [],
value: constants.MAX_MONEY
}],
locktime: 0
});
assert.ok(tx.isSane());
assert.ok(tx.checkInputs(0));
});
it('should fail on >51 bit fees', function () {
var tx = bcoin.tx({
version: 1,
inputs: [createInput(constants.MAX_MONEY + 1)],
outputs: [{
script: [],
value: 0
}],
locktime: 0
});
assert.ok(tx.isSane());
assert.ok(!tx.checkInputs(0));
});
it('should fail on >51 bit values from multiple', function () {
var tx = bcoin.tx({
version: 1,
inputs: [
createInput(Math.floor(constants.MAX_MONEY / 2)),
createInput(Math.floor(constants.MAX_MONEY / 2)),
createInput(Math.floor(constants.MAX_MONEY / 2))
],
outputs: [{
script: [],
value: constants.MAX_MONEY
}],
locktime: 0
});
assert.ok(tx.isSane());
assert.ok(!tx.checkInputs(0));
});
it('should fail on >51 bit fees from multiple', function () {
var tx = bcoin.tx({
version: 1,
inputs: [
createInput(Math.floor(constants.MAX_MONEY / 2)),
createInput(Math.floor(constants.MAX_MONEY / 2)),
createInput(Math.floor(constants.MAX_MONEY / 2))
],
outputs: [{
script: [],
value: 0
}],
locktime: 0
});
assert.ok(tx.isSane());
assert.ok(!tx.checkInputs(0));
});
it('should fail on >51 bit fees from multiple txs', function () {
var data = utils.merge(bcoin.network.get().genesis, { height: 0 });
var block = new bcoin.block(data);
for (var i = 0; i < 3; i++) {
var tx = bcoin.tx({
version: 1,
inputs: [
createInput(Math.floor(constants.MAX_MONEY / 2))
],
outputs: [{
script: [],
value: 0
}],
locktime: 0
});
block.txs.push(tx);
}
assert.equal(block.getReward(), -1);
});
it('should fail to parse >53 bit values', function () {
var tx = bcoin.tx({
version: 1,
inputs: [
createInput(Math.floor(constants.MAX_MONEY / 2))
],
outputs: [{
script: [],
value: 0
}],
locktime: 0
});
tx.outputs[0].value = new bn('00ffffffffffffff', 'hex');
assert(tx.outputs[0].value.bitLength() === 56);
var raw = tx.toRaw()
assert.throws(function() {
tx.fromRaw(raw);
});
tx.outputs[0].value = new bn('00ffffffffffffff', 'hex').ineg();
assert(tx.outputs[0].value.bitLength() === 56);
var raw = tx.toRaw()
assert.throws(function() {
tx.fromRaw(raw);
});
});
});

View File

@ -27,7 +27,7 @@ describe('Utils', function() {
btc = utils.btc(54678 * 1000000);
assert.equal(btc, '546.78');
btc = utils.btc(5460 * 10000000);
assert.equal(btc, '546.0');
assert.equal(btc, '546');
});
it('should convert btc to satoshi', function() {
@ -35,7 +35,76 @@ describe('Utils', function() {
assert(btc === 5460);
btc = utils.satoshi('546.78');
assert(btc === 54678 * 1000000);
btc = utils.satoshi('546.0');
btc = utils.satoshi('546');
assert(btc === 5460 * 10000000);
});
var unsigned = [
new bn('ffeeffee'),
new bn('001fffeeffeeffee'),
new bn('eeffeeff'),
new bn('001feeffeeffeeff'),
new bn(0),
new bn(1)
];
var signed = [
new bn('ffeeffee'),
new bn('001fffeeffeeffee'),
new bn('eeffeeff'),
new bn('001feeffeeffeeff'),
new bn(0),
new bn(1),
new bn('ffeeffee').ineg(),
new bn('001fffeeffeeffee').ineg(),
new bn('eeffeeff').ineg(),
new bn('001feeffeeffeeff').ineg(),
new bn(0).ineg(),
new bn(1).ineg()
];
unsigned.forEach(function(num) {
var buf1 = new Buffer(8);
var buf2 = new Buffer(8);
var msg = 'should write+read a ' + num.bitLength() + ' bit unsigned int';
it(msg, function() {
utils.writeU64(buf1, num, 0);
utils.writeU64N(buf2, num.toNumber(), 0);
assert.deepEqual(buf1, buf2);
var n1 = utils.readU64(buf1, 0);
var n2 = utils.readU64N(buf2, 0);
assert.equal(n1.toNumber(), n2);
});
});
signed.forEach(function(num) {
var buf1 = new Buffer(8);
var buf2 = new Buffer(8);
var msg = 'should write+read a ' + num.bitLength()
+ ' bit ' + (num.isNeg() ? 'negative' : 'positive') + ' int';
it(msg, function() {
utils.write64(buf1, num, 0);
utils.write64N(buf2, num.toNumber(), 0);
assert.deepEqual(buf1, buf2);
var n1 = utils.read64(buf1, 0);
var n2 = utils.read64N(buf2, 0);
assert.equal(n1.toNumber(), n2);
});
var msg = 'should write+read a ' + num.bitLength()
+ ' bit ' + (num.isNeg() ? 'negative' : 'positive') + ' int as unsigned';
it(msg, function() {
utils.writeU64(buf1, num, 0);
utils.writeU64N(buf2, num.toNumber(), 0);
assert.deepEqual(buf1, buf2);
var n1 = utils.readU64(buf1, 0);
if (num.isNeg()) {
assert.throws(function() {
utils.readU64N(buf2, 0);
});
} else {
var n2 = utils.readU64N(buf2, 0);
assert.equal(n1.toNumber(), n2);
}
});
});
});