241 lines
7.6 KiB
JavaScript
241 lines
7.6 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
var chai = require('chai');
|
||
|
var should = chai.should();
|
||
|
var sinon = require('sinon');
|
||
|
var async = require('async');
|
||
|
var proxyquire = require('proxyquire');
|
||
|
var memdown = require('memdown');
|
||
|
|
||
|
var bitcoindjs = require('../');
|
||
|
var DB = bitcoindjs.DB;
|
||
|
var Chain = bitcoindjs.Chain;
|
||
|
var Block = bitcoindjs.Block;
|
||
|
|
||
|
var chainData = require('./data/testnet-blocks.json');
|
||
|
|
||
|
describe('Bitcoin Chain', function() {
|
||
|
|
||
|
describe('@constructor', function() {
|
||
|
|
||
|
it('can create a new instance with and without `new`', function() {
|
||
|
var chain = new Chain();
|
||
|
chain = Chain();
|
||
|
});
|
||
|
|
||
|
});
|
||
|
|
||
|
describe('#_writeBlock', function() {
|
||
|
it('should update hashes and call putBlock', function(done) {
|
||
|
var chain = new Chain();
|
||
|
chain.db = {
|
||
|
putBlock: sinon.stub().callsArg(1)
|
||
|
};
|
||
|
chain._writeBlock({hash: 'hash', prevHash: 'prevhash'}, function(err) {
|
||
|
should.not.exist(err);
|
||
|
chain.db.putBlock.callCount.should.equal(1);
|
||
|
chain.cache.hashes.hash.should.equal('prevhash');
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#_validateBlock', function() {
|
||
|
it('should call the callback', function(done) {
|
||
|
var chain = new Chain();
|
||
|
chain._validateBlock('block', function(err) {
|
||
|
should.not.exist(err);
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#getInterval', function() {
|
||
|
|
||
|
it('get default interval', function() {
|
||
|
var chain = new Chain();
|
||
|
chain.targetTimespan.toString(10).should.equal('1209600000');
|
||
|
chain.targetSpacing.toString(10).should.equal('600000');
|
||
|
chain.getDifficultyInterval().toString(10).should.equal('2016');
|
||
|
});
|
||
|
|
||
|
it('get custom interval', function() {
|
||
|
var chain = new Chain({
|
||
|
targetTimespan: 30 * 60 * 1000
|
||
|
});
|
||
|
chain.getDifficultyInterval().toString(10).should.equal('3');
|
||
|
});
|
||
|
|
||
|
});
|
||
|
|
||
|
describe('#buildGenesisBlock', function() {
|
||
|
it('can handle no options', function() {
|
||
|
var db = {
|
||
|
buildGenesisData: sinon.stub().returns({})
|
||
|
};
|
||
|
var chain = new Chain({db: db});
|
||
|
var block = chain.buildGenesisBlock();
|
||
|
should.exist(block);
|
||
|
block.should.be.instanceof(Block);
|
||
|
db.buildGenesisData.calledOnce.should.equal(true);
|
||
|
});
|
||
|
|
||
|
it('set timestamp, nonce, bits, merkleRoot and data of the genesis', function() {
|
||
|
var db = {
|
||
|
buildGenesisData: sinon.stub().returns({
|
||
|
merkleRoot: 'merkleRoot',
|
||
|
buffer: new Buffer('abcdef', 'hex')
|
||
|
})
|
||
|
};
|
||
|
var chain = new Chain({db: db});
|
||
|
var timestamp = '2015-03-20T14:46:01.118Z';
|
||
|
var block = chain.buildGenesisBlock({
|
||
|
timestamp: timestamp,
|
||
|
nonce: 1,
|
||
|
bits: 520617984
|
||
|
});
|
||
|
should.exist(block);
|
||
|
block.should.be.instanceof(Block);
|
||
|
block.timestamp.toISOString().should.equal(timestamp);
|
||
|
block.nonce.should.equal(1);
|
||
|
block.bits.should.equal(520617984);
|
||
|
block.merkleRoot.should.equal('merkleRoot');
|
||
|
block.data.should.deep.equal(new Buffer('abcdef', 'hex'));
|
||
|
db.buildGenesisData.calledOnce.should.equal(true);
|
||
|
});
|
||
|
|
||
|
});
|
||
|
|
||
|
describe('#getRetargetedBits', function() {
|
||
|
it('should get the correct bits', function() {
|
||
|
var chain = new Chain();
|
||
|
var bits = chain.getRetargetedBits(486604799, 12 * 24 * 60 * 60 * 1000);
|
||
|
bits.should.equal(484142299);
|
||
|
});
|
||
|
it('should get the correct bits if actual timespan was really small', function() {
|
||
|
var chain = new Chain();
|
||
|
var bits1 = chain.getRetargetedBits(486604799, 2 * 24 * 60 * 60 * 1000);
|
||
|
bits1.should.equal(473956288);
|
||
|
var bits2 = chain.getRetargetedBits(486604799, 1 * 24 * 60 * 60 * 1000);
|
||
|
bits2.should.equal(473956288);
|
||
|
});
|
||
|
it('should get the correct bits if actual timespan was really large', function() {
|
||
|
var chain = new Chain();
|
||
|
var bits1 = chain.getRetargetedBits(436567560, 60 * 24 * 60 * 60 * 1000);
|
||
|
bits1.should.equal(437647392);
|
||
|
var bits2 = chain.getRetargetedBits(436567560, 70 * 24 * 60 * 60 * 1000);
|
||
|
bits2.should.equal(437647392);
|
||
|
});
|
||
|
it('should not give higher than max bits', function() {
|
||
|
var chain = new Chain();
|
||
|
var bits = chain.getRetargetedBits(486604799, 16 * 24 * 60 * 60 * 1000);
|
||
|
bits.should.equal(486604799);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#getTargetFromBits/#getBitsFromTarget', function() {
|
||
|
|
||
|
var target1;
|
||
|
var target2;
|
||
|
var target3;
|
||
|
|
||
|
it('should calculate the target correctly', function() {
|
||
|
var chain = new Chain();
|
||
|
var target1 = chain.getTargetFromBits(0x1b0404cb);
|
||
|
var expected = '00000000000404cb000000000000000000000000000000000000000000000000';
|
||
|
target1.toString('hex', 32).should.equal(expected);
|
||
|
});
|
||
|
|
||
|
it('should error if bits is too small', function() {
|
||
|
var chain = new Chain();
|
||
|
(function(){
|
||
|
var target1 = chain.getTargetFromBits(Chain.DEFAULTS.MIN_BITS - 1);
|
||
|
}).should.throw('bits is too small');
|
||
|
});
|
||
|
|
||
|
it('should error if bits is too large', function() {
|
||
|
var chain = new Chain();
|
||
|
(function(){
|
||
|
var target1 = chain.getTargetFromBits(Chain.DEFAULTS.MAX_BITS + 1);
|
||
|
}).should.throw('bits is too big');
|
||
|
});
|
||
|
|
||
|
it('should get the bits', function() {
|
||
|
var chain = new Chain();
|
||
|
var expected = '00000000000404cb000000000000000000000000000000000000000000000000';
|
||
|
var bits = chain.getBitsFromTarget(expected);
|
||
|
bits.should.equal(0x1b0404cb);
|
||
|
});
|
||
|
|
||
|
});
|
||
|
|
||
|
describe('#getDifficultyFromBits', function() {
|
||
|
it('should return the correct difficulty', function() {
|
||
|
var genesis = {bits: 0x1d00ffff};
|
||
|
var chain = new Chain({genesis: genesis});
|
||
|
|
||
|
var difficulty = chain.getDifficultyFromBits(0x1818bb87);
|
||
|
difficulty.toString(10).should.equal('44455415962');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#getBlockWeight', function() {
|
||
|
it('should return the correct block weight for normal targets', function(done) {
|
||
|
var block = {bits: 0x1d00ffff};
|
||
|
var db = {
|
||
|
getBlock: sinon.stub().callsArgWith(1, null, block)
|
||
|
};
|
||
|
var chain = new Chain({db: db});
|
||
|
chain.getBlockWeight(block, function(err, weight) {
|
||
|
weight.toString(16).should.equal('100010001');
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
it('should correctly report an error if it happens', function(done) {
|
||
|
var block = {bits: 0x1d00ffff};
|
||
|
var db = {
|
||
|
getBlock: sinon.stub().callsArgWith(1, new Error('fake error'))
|
||
|
};
|
||
|
var chain = new Chain({db: db});
|
||
|
chain.getBlockWeight(block, function(err, weight) {
|
||
|
should.exist(err);
|
||
|
err.message.should.equal('fake error');
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
it('should correctly report an error for a null block', function(done) {
|
||
|
var block = {bits: 0x1d00ffff};
|
||
|
var db = {
|
||
|
getBlock: sinon.stub().callsArgWith(1, null, null)
|
||
|
};
|
||
|
var chain = new Chain({db: db});
|
||
|
chain.getBlockWeight(block, function(err, weight) {
|
||
|
should.exist(err);
|
||
|
err.message.should.match(/Block not found/);
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('Bitcoin POW', function() {
|
||
|
it('should calculate correct difficulty for block 201600', function(done) {
|
||
|
var chain = new Chain();
|
||
|
var beginBlock = {
|
||
|
timestamp: new Date(1348092851000),
|
||
|
bits: 436591499
|
||
|
};
|
||
|
var lastBlock = {
|
||
|
timestamp: new Date(1349227021000),
|
||
|
bits: 436591499
|
||
|
};
|
||
|
chain.getHeightForBlock = sinon.stub().callsArgWith(1, null, 201599);
|
||
|
chain.getBlockAtHeight = sinon.stub().callsArgWith(2, null, beginBlock);
|
||
|
chain.getNextWorkRequired(lastBlock, function(err, bits) {
|
||
|
should.not.exist(err);
|
||
|
bits.should.equal(436567560);
|
||
|
done();
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|