bitcore-node-zcash/test/services/db.unit.js

1016 lines
32 KiB
JavaScript
Raw Normal View History

2015-07-16 12:53:44 -07:00
'use strict';
var should = require('chai').should();
var sinon = require('sinon');
2015-08-31 06:00:00 -07:00
var EventEmitter = require('events').EventEmitter;
var proxyquire = require('proxyquire');
2015-08-31 06:00:00 -07:00
var index = require('../../');
2015-08-31 06:00:00 -07:00
var DB = index.services.DB;
2015-08-31 06:00:00 -07:00
var blockData = require('../data/livenet-345003.json');
2015-08-27 07:10:07 -07:00
var bitcore = require('bitcore');
2015-08-31 06:00:00 -07:00
var Networks = bitcore.Networks;
2015-08-27 07:10:07 -07:00
var Block = bitcore.Block;
2015-08-31 06:00:00 -07:00
var BufferUtil = bitcore.util.buffer;
2015-08-31 06:00:00 -07:00
var transactionData = require('../data/bitcoin-transactions.json');
2015-08-31 06:00:00 -07:00
var chainHashes = require('../data/hashes.json');
var chainData = require('../data/testnet-blocks.json');
2015-08-27 07:10:07 -07:00
var errors = index.errors;
2015-07-16 12:53:44 -07:00
var memdown = require('memdown');
2015-09-01 13:42:09 -07:00
var levelup = require('levelup');
2015-08-14 13:44:40 -07:00
var bitcore = require('bitcore');
var Transaction = bitcore.Transaction;
2015-07-16 12:53:44 -07:00
2015-08-31 06:00:00 -07:00
describe('DB Service', function() {
2015-08-31 06:00:00 -07:00
2015-08-31 06:00:00 -07:00
function hexlebuf(hexString){
return BufferUtil.reverse(new Buffer(hexString, 'hex'));
}
function lebufhex(buf) {
return BufferUtil.reverse(buf).toString('hex');
}
2015-08-31 06:00:00 -07:00
var baseConfig = {
node: {
network: Networks.testnet,
datadir: 'testdir'
},
store: memdown
};
describe('#_setDataPath', function() {
it('should set the database path', function() {
var config = {
node: {
network: Networks.livenet,
datadir: process.env.HOME + '/.bitcoin'
},
store: memdown
};
var db = new DB(config);
db.dataPath.should.equal(process.env.HOME + '/.bitcoin/bitcore-node.db');
});
it('should load the db for testnet', function() {
var config = {
node: {
network: Networks.testnet,
datadir: process.env.HOME + '/.bitcoin'
},
store: memdown
};
var db = new DB(config);
db.dataPath.should.equal(process.env.HOME + '/.bitcoin/testnet3/bitcore-node.db');
});
it('error with unknown network', function() {
var config = {
node: {
network: 'unknown',
datadir: process.env.HOME + '/.bitcoin'
},
store: memdown
};
(function() {
var db = new DB(config);
}).should.throw('Unknown network');
});
it('should load the db with regtest', function() {
// Switch to use regtest
2015-08-31 06:00:00 -07:00
// Networks.remove(Networks.testnet);
2015-08-31 06:00:00 -07:00
Networks.add({
name: 'regtest',
alias: 'regtest',
pubkeyhash: 0x6f,
privatekey: 0xef,
scripthash: 0xc4,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
networkMagic: 0xfabfb5da,
port: 18444,
dnsSeeds: [ ]
});
var regtest = Networks.get('regtest');
var config = {
node: {
network: regtest,
datadir: process.env.HOME + '/.bitcoin'
},
store: memdown
};
var db = new DB(config);
db.dataPath.should.equal(process.env.HOME + '/.bitcoin/regtest/bitcore-node.db');
Networks.remove(regtest);
});
});
2015-07-16 12:53:44 -07:00
2015-08-21 13:13:11 -07:00
describe('#start', function() {
2015-08-31 06:00:00 -07:00
var TestDB;
var genesisBuffer;
before(function() {
2015-08-31 06:00:00 -07:00
TestDB = proxyquire('../../lib/services/db', {
2015-08-31 06:00:00 -07:00
fs: {
existsSync: sinon.stub().returns(true)
},
levelup: sinon.stub()
});
genesisBuffer = new Buffer('0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000', 'hex');
});
2015-08-14 13:44:40 -07:00
it('should emit ready', function(done) {
2015-08-31 06:00:00 -07:00
var db = new TestDB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-31 06:00:00 -07:00
on: sinon.spy(),
genesisBuffer: genesisBuffer
2015-08-14 13:44:40 -07:00
};
2015-08-31 06:00:00 -07:00
db.getMetadata = sinon.stub().callsArg(0);
db.connectBlock = sinon.stub().callsArg(1);
db.saveMetadata = sinon.stub();
db.sync = sinon.stub();
2015-08-21 13:13:11 -07:00
var readyFired = false;
db.on('ready', function() {
readyFired = true;
});
db.start(function() {
readyFired.should.equal(true);
done();
});
});
2015-08-31 06:00:00 -07:00
it('genesis block if no metadata is found in the db', function(done) {
var node = {
network: Networks.testnet,
datadir: 'testdir',
2015-08-31 06:00:00 -07:00
services: {
2015-08-31 06:00:00 -07:00
bitcoind: {
genesisBuffer: genesisBuffer,
on: sinon.stub()
}
}
};
var db = new TestDB({node: node});
db.getMetadata = sinon.stub().callsArgWith(0, null, null);
db.connectBlock = sinon.stub().callsArg(1);
db.saveMetadata = sinon.stub();
db.sync = sinon.stub();
db.start(function() {
should.exist(db.tip);
db.tip.hash.should.equal('00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206');
done();
});
});
it('metadata from the database if it exists', function(done) {
var tipHash = '00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206';
2015-08-31 06:00:00 -07:00
var node = {
network: Networks.testnet,
datadir: 'testdir',
2015-08-31 06:00:00 -07:00
services: {
2015-08-31 06:00:00 -07:00
bitcoind: {
genesisBuffer: genesisBuffer,
getBlockIndex: sinon.stub().returns({tip:tipHash}),
2015-08-31 06:00:00 -07:00
on: sinon.stub()
}
}
};
var tip = Block.fromBuffer(genesisBuffer);
var db = new TestDB({node: node});
db.getMetadata = sinon.stub().callsArgWith(0, null, {
tip: tipHash,
tipHeight: 0
});
db.getBlock = sinon.stub().callsArgWith(1, null, tip);
db.saveMetadata = sinon.stub();
db.sync = sinon.stub();
db.start(function() {
should.exist(db.tip);
db.tip.hash.should.equal(tipHash);
done();
});
});
it('emit error from getMetadata', function(done) {
var node = {
network: Networks.testnet,
datadir: 'testdir',
2015-08-31 06:00:00 -07:00
services: {
2015-08-31 06:00:00 -07:00
bitcoind: {
genesisBuffer: genesisBuffer,
on: sinon.stub()
}
}
};
var db = new TestDB({node: node});
db.getMetadata = sinon.stub().callsArgWith(0, new Error('test'));
db.start(function(err) {
should.exist(err);
err.message.should.equal('test');
done();
});
});
it('emit error from getBlock', function(done) {
var node = {
network: Networks.testnet,
datadir: 'testdir',
2015-08-31 06:00:00 -07:00
services: {
2015-08-31 06:00:00 -07:00
bitcoind: {
genesisBuffer: genesisBuffer,
on: sinon.stub()
}
}
};
var db = new TestDB({node: node});
var tipHash = '00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206';
db.getMetadata = sinon.stub().callsArgWith(0, null, {
tip: tipHash,
tipHeigt: 0
});
db.getBlock = sinon.stub().callsArgWith(1, new Error('test'));
db.start(function(err) {
should.exist(err);
err.message.should.equal('test');
done();
});
});
it('will call sync when there is a new tip', function(done) {
var db = new TestDB(baseConfig);
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = new EventEmitter();
db.node.services.bitcoind.genesisBuffer = genesisBuffer;
2015-08-31 06:00:00 -07:00
db.getMetadata = sinon.stub().callsArg(0);
db.connectBlock = sinon.stub().callsArg(1);
db.saveMetadata = sinon.stub();
db.sync = sinon.stub();
db.start(function() {
db.sync = function() {
done();
};
2015-08-31 06:00:00 -07:00
db.node.services.bitcoind.emit('tip', 10);
2015-08-31 06:00:00 -07:00
});
});
it('will not call sync when there is a new tip and shutting down', function(done) {
var db = new TestDB(baseConfig);
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = new EventEmitter();
db.node.services.bitcoind.syncPercentage = sinon.spy();
db.node.services.bitcoind.genesisBuffer = genesisBuffer;
2015-08-31 06:00:00 -07:00
db.getMetadata = sinon.stub().callsArg(0);
db.connectBlock = sinon.stub().callsArg(1);
db.saveMetadata = sinon.stub();
db.node.stopping = true;
db.sync = sinon.stub();
db.start(function() {
db.sync.callCount.should.equal(1);
2015-08-31 06:00:00 -07:00
db.node.services.bitcoind.once('tip', function() {
2015-08-31 06:00:00 -07:00
db.sync.callCount.should.equal(1);
done();
});
2015-08-31 06:00:00 -07:00
db.node.services.bitcoind.emit('tip', 10);
2015-08-31 06:00:00 -07:00
});
});
2015-08-21 13:13:11 -07:00
});
describe('#stop', function() {
it('should wait until db has stopped syncing before closing leveldb', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.store = {
close: sinon.stub().callsArg(0)
};
db.bitcoindSyncing = true;
2015-08-21 13:13:11 -07:00
db.stop(function(err) {
should.not.exist(err);
done();
});
setTimeout(function() {
db.bitcoindSyncing = false;
}, 15);
2015-08-14 13:44:40 -07:00
});
});
describe('#getTransaction', function() {
it('will return a NotFound error', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
getTransaction: sinon.stub().callsArgWith(2, null, null)
};
var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623';
db.getTransaction(txid, true, function(err) {
err.should.be.instanceof(errors.Transaction.NotFound);
done();
});
});
it('will return an error from bitcoind', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
getTransaction: sinon.stub().callsArgWith(2, new Error('test error'))
};
var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623';
db.getTransaction(txid, true, function(err) {
err.message.should.equal('test error');
done();
});
});
it('will return an error from bitcoind', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
getTransaction: sinon.stub().callsArgWith(2, null, new Buffer(transactionData[0].hex, 'hex'))
};
var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623';
db.getTransaction(txid, true, function(err, tx) {
if (err) {
throw err;
}
should.exist(tx);
done();
});
});
});
2015-07-16 12:53:44 -07:00
describe('#getBlock', function() {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
2015-08-27 07:10:07 -07:00
var blockBuffer = new Buffer(blockData, 'hex');
var expectedBlock = Block.fromBuffer(blockBuffer);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-27 07:10:07 -07:00
getBlock: sinon.stub().callsArgWith(1, null, blockBuffer)
2015-07-16 12:53:44 -07:00
};
2015-08-27 07:10:07 -07:00
it('should get the block from bitcoin daemon', function(done) {
2015-07-16 12:53:44 -07:00
db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) {
should.not.exist(err);
2015-08-27 07:10:07 -07:00
block.hash.should.equal(expectedBlock.hash);
2015-07-16 12:53:44 -07:00
done();
});
});
it('should give an error when bitcoind.js gives an error', function(done) {
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {};
db.node.services.bitcoind.getBlock = sinon.stub().callsArgWith(1, new Error('error'));
2015-07-16 12:53:44 -07:00
db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) {
should.exist(err);
err.message.should.equal('error');
done();
});
});
});
2015-09-10 10:54:40 -07:00
describe('#getBlockHashesByTimestamp', function() {
it('should get the correct block hashes', function(done) {
var db = new DB(baseConfig);
var readStream = new EventEmitter();
db.store = {
createReadStream: sinon.stub().returns(readStream)
};
var block1 = {
hash: '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b',
timestamp: 1441911909
};
var block2 = {
hash: '000000000383752a55a0b2891ce018fd0fdc0b6352502772b034ec282b4a1bf6',
timestamp: 1441913112
};
db.getBlockHashesByTimestamp(1441914000, 1441911000, function(err, hashes) {
2015-09-10 10:54:40 -07:00
should.not.exist(err);
hashes.should.deep.equal([block2.hash, block1.hash]);
2015-09-10 10:54:40 -07:00
done();
});
readStream.emit('data', {
key: db._encodeBlockIndexKey(block2.timestamp),
value: db._encodeBlockIndexValue(block2.hash)
2015-09-10 10:54:40 -07:00
});
readStream.emit('data', {
key: db._encodeBlockIndexKey(block1.timestamp),
value: db._encodeBlockIndexValue(block1.hash)
2015-09-10 10:54:40 -07:00
});
readStream.emit('close');
});
it('should give an error if the stream has an error', function(done) {
var db = new DB(baseConfig);
var readStream = new EventEmitter();
db.store = {
createReadStream: sinon.stub().returns(readStream)
};
db.getBlockHashesByTimestamp(1441911000, 1441914000, function(err, hashes) {
should.exist(err);
err.message.should.equal('error');
done();
});
readStream.emit('error', new Error('error'));
readStream.emit('close');
});
2015-09-16 09:04:44 -07:00
it('should give an error if the timestamp is out of range', function(done) {
var db = new DB(baseConfig);
var readStream = new EventEmitter();
db.store = {
createReadStream: sinon.stub().returns(readStream)
};
db.getBlockHashesByTimestamp(-1, -5, function(err, hashes) {
should.exist(err);
err.message.should.equal('Invalid Argument: timestamp out of bounds');
done();
});
});
2015-09-10 10:54:40 -07:00
});
2015-08-14 13:44:40 -07:00
describe('#getPrevHash', function() {
it('should return prevHash from bitcoind', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
getBlockIndex: sinon.stub().returns({
prevHash: 'prevhash'
})
};
db.getPrevHash('hash', function(err, prevHash) {
should.not.exist(err);
prevHash.should.equal('prevhash');
done();
});
});
it('should give an error if bitcoind could not find it', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
getBlockIndex: sinon.stub().returns(null)
};
db.getPrevHash('hash', function(err, prevHash) {
should.exist(err);
done();
});
});
});
describe('#getTransactionWithBlockInfo', function() {
it('should give a transaction with height and timestamp', function(done) {
var txBuffer = new Buffer('01000000016f95980911e01c2c664b3e78299527a47933aac61a515930a8fe0213d1ac9abe01000000da0047304402200e71cda1f71e087c018759ba3427eb968a9ea0b1decd24147f91544629b17b4f0220555ee111ed0fc0f751ffebf097bdf40da0154466eb044e72b6b3dcd5f06807fa01483045022100c86d6c8b417bff6cc3bbf4854c16bba0aaca957e8f73e19f37216e2b06bb7bf802205a37be2f57a83a1b5a8cc511dc61466c11e9ba053c363302e7b99674be6a49fc0147522102632178d046673c9729d828cfee388e121f497707f810c131e0d3fc0fe0bd66d62103a0951ec7d3a9da9de171617026442fcd30f34d66100fab539853b43f508787d452aeffffffff0240420f000000000017a9148a31d53a448c18996e81ce67811e5fb7da21e4468738c9d6f90000000017a9148ce5408cfeaddb7ccb2545ded41ef478109454848700000000', 'hex');
var info = {
blockHash: '00000000000ec715852ea2ecae4dc8563f62d603c820f81ac284cd5be0a944d6',
2015-08-14 13:44:40 -07:00
height: 530482,
timestamp: 1439559434000,
buffer: txBuffer
};
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, info)
};
db.getTransactionWithBlockInfo('2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f', true, function(err, tx) {
should.not.exist(err);
tx.__blockHash.should.equal(info.blockHash);
2015-08-14 13:44:40 -07:00
tx.__height.should.equal(info.height);
tx.__timestamp.should.equal(info.timestamp);
done();
});
});
it('should give an error if one occurred', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, new Error('error'))
};
db.getTransactionWithBlockInfo('tx', true, function(err, tx) {
should.exist(err);
done();
});
});
});
describe('#sendTransaction', function() {
it('should give the txid on success', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
sendTransaction: sinon.stub().returns('txid')
};
var tx = new Transaction();
db.sendTransaction(tx, function(err, txid) {
should.not.exist(err);
txid.should.equal('txid');
done();
});
});
it('should give an error if bitcoind threw an error', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
sendTransaction: sinon.stub().throws(new Error('error'))
};
var tx = new Transaction();
db.sendTransaction(tx, function(err, txid) {
should.exist(err);
done();
});
});
});
2015-09-01 11:29:04 -07:00
describe('#estimateFee', function() {
2015-08-14 13:44:40 -07:00
it('should pass along the fee from bitcoind', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-14 13:44:40 -07:00
estimateFee: sinon.stub().returns(1000)
};
db.estimateFee(5, function(err, fee) {
should.not.exist(err);
fee.should.equal(1000);
2015-08-31 06:00:00 -07:00
db.node.services.bitcoind.estimateFee.args[0][0].should.equal(5);
2015-08-14 13:44:40 -07:00
done();
});
});
});
2015-07-16 12:53:44 -07:00
2015-09-01 11:29:04 -07:00
describe('#saveMetadata', function() {
it('will emit an error with default callback', function(done) {
var db = new DB(baseConfig);
db.cache = {
hashes: {},
chainHashes: {}
};
db.tip = {
hash: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
__height: 0
};
db.store = {
put: sinon.stub().callsArgWith(3, new Error('test'))
};
db.on('error', function(err) {
err.message.should.equal('test');
done();
});
db.saveMetadata();
});
it('will give an error with callback', function(done) {
var db = new DB(baseConfig);
db.cache = {
hashes: {},
chainHashes: {}
};
db.tip = {
hash: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
__height: 0
};
db.store = {
put: sinon.stub().callsArgWith(3, new Error('test'))
};
db.saveMetadata(function(err) {
err.message.should.equal('test');
done();
});
});
it('will call store with the correct arguments', function(done) {
var db = new DB(baseConfig);
db.cache = {
hashes: {},
chainHashes: {}
};
db.tip = {
hash: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
__height: 0
};
db.store = {
put: function(key, value, options, callback) {
key.should.equal('metadata');
JSON.parse(value).should.deep.equal({
tip: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
2015-09-01 11:29:04 -07:00
});
options.should.deep.equal({});
callback.should.be.a('function');
done();
}
};
db.saveMetadata();
});
});
2015-09-01 13:42:09 -07:00
describe('#getMetadata', function() {
it('will get metadata', function() {
var db = new DB(baseConfig);
var json = JSON.stringify({
tip: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
tipHeight: 101,
cache: {
hashes: {},
chainHashes: {}
}
});
db.store = {};
db.store.get = sinon.stub().callsArgWith(2, null, json);
db.getMetadata(function(err, data) {
data.tip.should.equal('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f');
data.tipHeight.should.equal(101);
data.cache.should.deep.equal({
hashes: {},
chainHashes: {}
});
});
});
it('will handle a notfound error from leveldb', function() {
var db = new DB(baseConfig);
db.store = {};
var error = new levelup.errors.NotFoundError();
db.store.get = sinon.stub().callsArgWith(2, error);
db.getMetadata(function(err, data) {
should.not.exist(err);
data.should.deep.equal({});
});
});
it('will handle error from leveldb', function() {
var db = new DB(baseConfig);
db.store = {};
db.store.get = sinon.stub().callsArgWith(2, new Error('test'));
db.getMetadata(function(err) {
err.message.should.equal('test');
});
});
it('give an error when parsing invalid json', function() {
var db = new DB(baseConfig);
db.store = {};
db.store.get = sinon.stub().callsArgWith(2, null, '{notvalid@json}');
db.getMetadata(function(err) {
err.message.should.equal('Could not parse metadata');
});
});
});
2015-08-31 06:00:00 -07:00
describe('#connectBlock', function() {
2015-07-23 08:33:42 -07:00
it('should remove block from mempool and call blockHandler with true', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
2015-07-23 08:33:42 -07:00
db.mempool = {
removeBlock: sinon.stub()
2015-07-16 12:53:44 -07:00
};
2015-08-31 06:00:00 -07:00
db.runAllBlockHandlers = sinon.stub().callsArg(2);
db.connectBlock({hash: 'hash'}, function(err) {
2015-07-16 12:53:44 -07:00
should.not.exist(err);
2015-08-31 06:00:00 -07:00
db.runAllBlockHandlers.args[0][1].should.equal(true);
2015-07-16 12:53:44 -07:00
done();
});
});
});
2015-08-31 06:00:00 -07:00
describe('#disconnectBlock', function() {
2015-07-23 08:33:42 -07:00
it('should call blockHandler with false', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.runAllBlockHandlers = sinon.stub().callsArg(2);
db.disconnectBlock({hash: 'hash'}, function(err) {
2015-07-16 12:53:44 -07:00
should.not.exist(err);
2015-08-31 06:00:00 -07:00
db.runAllBlockHandlers.args[0][1].should.equal(false);
2015-07-16 12:53:44 -07:00
done();
});
});
});
2015-08-31 06:00:00 -07:00
describe('#runAllBlockHandlers', function() {
var db = new DB(baseConfig);
2015-08-31 06:00:00 -07:00
var Service1 = function() {};
Service1.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op1', 'op2', 'op3']);
var Service2 = function() {};
Service2.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op4', 'op5']);
2015-09-09 11:30:25 -07:00
var Service3 = function() {};
var Service4 = function() {};
Service4.prototype.blockHandler = sinon.stub().callsArgWith(2, null, 'bad-value');
2015-08-27 13:09:27 -07:00
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {
service1: new Service1(),
service2: new Service2()
};
2015-07-16 12:53:44 -07:00
db.store = {
batch: sinon.stub().callsArg(1)
};
2015-09-10 10:54:40 -07:00
var block = {
hash: '00000000000000000d0aaf93e464ddeb503655a0750f8b9c6eed0bdf0ccfc863',
header: {
timestamp: 1441906365
}
};
2015-08-31 06:00:00 -07:00
it('should call blockHandler in all services and perform operations', function(done) {
2015-09-10 10:54:40 -07:00
db.runAllBlockHandlers(block, true, function(err) {
2015-07-16 12:53:44 -07:00
should.not.exist(err);
2015-09-10 10:54:40 -07:00
var blockOp = {
type: 'put',
2015-09-15 13:40:31 -07:00
key: db._encodeBlockIndexKey(1441906365),
value: db._encodeBlockIndexValue('00000000000000000d0aaf93e464ddeb503655a0750f8b9c6eed0bdf0ccfc863')
};
2015-09-10 10:54:40 -07:00
db.store.batch.args[0][0].should.deep.equal([blockOp, 'op1', 'op2', 'op3', 'op4', 'op5']);
2015-07-16 12:53:44 -07:00
done();
});
});
2015-07-23 08:33:42 -07:00
2015-08-31 06:00:00 -07:00
it('should give an error if one of the services gives an error', function(done) {
var Service3 = function() {};
Service3.prototype.blockHandler = sinon.stub().callsArgWith(2, new Error('error'));
db.node.services.service3 = new Service3();
2015-07-23 08:33:42 -07:00
2015-09-10 10:54:40 -07:00
db.runAllBlockHandlers(block, true, function(err) {
2015-07-16 12:53:44 -07:00
should.exist(err);
done();
});
});
2015-09-09 11:30:25 -07:00
it('should not give an error if a service does not have blockHandler', function(done) {
db.node = {};
db.node.services = {
service3: new Service3()
};
2015-09-10 10:54:40 -07:00
db.runAllBlockHandlers(block, true, function(err) {
2015-09-09 11:30:25 -07:00
should.not.exist(err);
done();
});
});
it('should throw an error if blockHandler gives unexpected result', function() {
db.node = {};
db.node.services = {
service4: new Service4()
};
(function() {
2015-09-10 10:54:40 -07:00
db.runAllBlockHandlers(block, true, function(err) {
2015-09-09 11:30:25 -07:00
should.not.exist(err);
});
}).should.throw('bitcore.ErrorInvalidArgument');
});
2015-07-16 12:53:44 -07:00
});
describe('#getAPIMethods', function() {
2015-07-23 08:33:42 -07:00
it('should return the correct db methods', function() {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
2015-08-27 13:09:27 -07:00
db.node = {};
2015-08-31 06:00:00 -07:00
db.node.services = {};
2015-07-16 12:53:44 -07:00
var methods = db.getAPIMethods();
2015-09-10 10:54:40 -07:00
methods.length.should.equal(6);
2015-07-16 12:53:44 -07:00
});
});
2015-08-31 06:00:00 -07:00
describe('#findCommonAncestor', function() {
it('will find an ancestor 6 deep', function(done) {
2015-08-31 06:00:00 -07:00
var db = new DB(baseConfig);
db.tip = {
hash: chainHashes[chainHashes.length - 1]
2015-08-31 06:00:00 -07:00
};
2015-08-31 06:00:00 -07:00
var expectedAncestor = chainHashes[chainHashes.length - 6];
var mainBlocks = {};
for(var i = chainHashes.length - 1; i > chainHashes.length - 10; i--) {
var hash = chainHashes[i];
var prevHash = hexlebuf(chainHashes[i - 1]);
mainBlocks[hash] = {
header: {
prevHash: prevHash
}
};
}
2015-08-31 06:00:00 -07:00
var forkedBlocks = {
'd7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82': {
header: {
prevHash: hexlebuf('76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a')
},
hash: 'd7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82'
2015-08-31 06:00:00 -07:00
},
'76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a': {
header: {
prevHash: hexlebuf('f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c')
},
hash: '76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a'
2015-08-31 06:00:00 -07:00
},
'f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c': {
header: {
prevHash: hexlebuf('2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31')
},
hash: 'f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c'
2015-08-31 06:00:00 -07:00
},
'2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31': {
header: {
prevHash: hexlebuf('adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453')
},
hash: '2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31'
2015-08-31 06:00:00 -07:00
},
'adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453': {
header: {
prevHash: hexlebuf('3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618')
},
hash: 'adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453'
2015-08-31 06:00:00 -07:00
},
'3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618': {
header: {
prevHash: hexlebuf(expectedAncestor)
},
hash: '3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618'
}
2015-08-31 06:00:00 -07:00
};
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-31 06:00:00 -07:00
getBlockIndex: function(hash) {
var forkedBlock = forkedBlocks[hash];
var mainBlock = mainBlocks[hash];
var prevHash;
if (forkedBlock && forkedBlock.header.prevHash) {
prevHash = BufferUtil.reverse(forkedBlock.header.prevHash).toString('hex');
} else if (mainBlock && mainBlock.header.prevHash){
prevHash = BufferUtil.reverse(mainBlock.header.prevHash).toString('hex');
} else {
return null;
}
2015-08-31 06:00:00 -07:00
return {
prevHash: prevHash
2015-08-31 06:00:00 -07:00
};
}
};
var block = forkedBlocks['d7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82'];
db.findCommonAncestor(block, function(err, ancestorHash) {
if (err) {
throw err;
}
ancestorHash.should.equal(expectedAncestor);
done();
2015-08-31 06:00:00 -07:00
});
});
});
describe('#syncRewind', function() {
it('will undo blocks 6 deep', function() {
var db = new DB(baseConfig);
var ancestorHash = chainHashes[chainHashes.length - 6];
db.tip = {
__height: 10,
hash: chainHashes[chainHashes.length],
header: {
prevHash: hexlebuf(chainHashes[chainHashes.length - 1])
}
};
db.saveMetadata = sinon.stub();
db.emit = sinon.stub();
db.getBlock = function(hash, callback) {
setImmediate(function() {
for(var i = chainHashes.length; i > 0; i--) {
var block = {
hash: chainHashes[i],
header: {
prevHash: hexlebuf(chainHashes[i - 1])
}
};
if (chainHashes[i] === hash) {
callback(null, block);
}
}
});
};
2015-08-31 06:00:00 -07:00
db.node.services = {};
2015-08-31 06:00:00 -07:00
db.disconnectBlock = function(block, callback) {
setImmediate(callback);
};
db.findCommonAncestor = function(block, callback) {
setImmediate(function() {
callback(null, ancestorHash);
});
};
var forkedBlock = {};
db.syncRewind(forkedBlock, function(err) {
if (err) {
throw err;
}
db.tip.__height.should.equal(4);
db.tip.hash.should.equal(ancestorHash);
});
});
});
describe('#sync', function() {
var node = new EventEmitter();
var syncConfig = {
node: node,
store: memdown
};
syncConfig.node.network = Networks.testnet;
syncConfig.node.datadir = 'testdir';
it('will get and add block up to the tip height', function(done) {
var db = new DB(syncConfig);
var blockBuffer = new Buffer(blockData, 'hex');
var block = Block.fromBuffer(blockBuffer);
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.runAllMempoolIndexes = sinon.stub().callsArg(0);
2015-08-31 06:00:00 -07:00
db.node.services.bitcoind = {
2015-08-31 06:00:00 -07:00
getBlock: sinon.stub().callsArgWith(1, null, blockBuffer),
isSynced: sinon.stub().returns(true),
height: 1
};
db.tip = {
__height: 0,
hash: lebufhex(block.header.prevHash)
};
db.saveMetadata = sinon.stub();
db.emit = sinon.stub();
db.cache = {
hashes: {}
};
db.connectBlock = function(block, callback) {
db.tip.__height += 1;
callback();
};
db.node.once('synced', function() {
db.runAllMempoolIndexes.callCount.should.equal(1);
2015-08-31 06:00:00 -07:00
done();
});
db.sync();
});
it('will exit and emit error with error from bitcoind.getBlock', function(done) {
var db = new DB(syncConfig);
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-31 06:00:00 -07:00
getBlock: sinon.stub().callsArgWith(1, new Error('test error')),
height: 1
};
db.tip = {
__height: 0
};
db.node.on('error', function(err) {
err.message.should.equal('test error');
done();
});
db.sync();
});
it('will stop syncing when the node is stopping', function(done) {
var db = new DB(syncConfig);
var blockBuffer = new Buffer(blockData, 'hex');
var block = Block.fromBuffer(blockBuffer);
2015-08-31 06:00:00 -07:00
db.node.services = {};
db.node.services.bitcoind = {
2015-08-31 06:00:00 -07:00
getBlock: sinon.stub().callsArgWith(1, null, blockBuffer),
isSynced: sinon.stub().returns(true),
height: 1
};
db.tip = {
__height: 0,
hash: block.prevHash
};
db.saveMetadata = sinon.stub();
db.emit = sinon.stub();
db.cache = {
hashes: {}
};
db.connectBlock = function(block, callback) {
db.tip.__height += 1;
callback();
};
db.node.stopping = true;
var synced = false;
db.node.once('synced', function() {
synced = true;
});
db.sync();
setTimeout(function() {
synced.should.equal(false);
done();
}, 10);
});
});
2015-07-16 12:53:44 -07:00
});