diff --git a/bin/start-libbitcoind.js b/bin/start-libbitcoind.js index a7f7299d..a2860ce2 100644 --- a/bin/start-libbitcoind.js +++ b/bin/start-libbitcoind.js @@ -2,17 +2,21 @@ 'use strict'; -var chainlib = require('chainlib'); -var log = chainlib.log; +var index = require('..'); +var log = index.log; process.title = 'libbitcoind'; /** * daemon */ -var daemon = require('../').daemon({ - datadir: process.env.BITCORENODE_DIR || '~/.bitcoin', - network: process.env.BITCORENODE_NETWORK || 'livenet' +var daemon = require('../').modules.BitcoinModule({ + node: { + datadir: process.env.BITCORENODE_DIR || process.env.HOME + '/.bitcoin', + network: { + name: process.env.BITCORENODE_NETWORK || 'livenet' + } + } }); daemon.start(function() { @@ -54,4 +58,4 @@ function exitHandler(options, err) { process.on('uncaughtException', exitHandler.bind(null, {exit:true})); //catches ctrl+c event -process.on('SIGINT', exitHandler.bind(null, {sigint:true})); \ No newline at end of file +process.on('SIGINT', exitHandler.bind(null, {sigint:true})); diff --git a/index.js b/index.js index 7225237e..7233605e 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,6 @@ 'use strict'; module.exports = require('./lib'); -module.exports.daemon = require('./lib/daemon'); module.exports.Node = require('./lib/node'); module.exports.Chain = require('./lib/chain'); module.exports.DB = require('./lib/db'); @@ -11,6 +10,7 @@ module.exports.errors = require('./lib/errors'); module.exports.modules = {}; module.exports.modules.AddressModule = require('./lib/modules/address'); +module.exports.modules.BitcoinModule = require('./lib/modules/bitcoind'); module.exports.scaffold = {}; module.exports.scaffold.create = require('./lib/scaffold/create'); diff --git a/integration/regtest-node.js b/integration/regtest-node.js index ebd94108..ef2bcaa1 100644 --- a/integration/regtest-node.js +++ b/integration/regtest-node.js @@ -23,7 +23,10 @@ var node; var should = chai.should(); var BitcoinRPC = require('bitcoind-rpc'); -var BitcoreNode = require('..').Node; +var index = require('..'); +var BitcoreNode = index.Node; +var AddressModule = index.modules.AddressModule; +var BitcoinModule = index.modules.BitcoinModule; var testWIF = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; var testKey; var client; @@ -60,7 +63,19 @@ describe('Node Functionality', function() { var configuration = { datadir: datadir, - network: 'regtest' + network: 'regtest', + modules: [ + { + name: 'bitcoind', + module: BitcoinModule, + dependencies: BitcoinModule.dependencies + }, + { + name: 'address', + module: AddressModule, + dependencies: AddressModule.dependencies + } + ] }; node = new BitcoreNode(configuration); @@ -102,7 +117,10 @@ describe('Node Functionality', function() { after(function(done) { this.timeout(20000); - node.bitcoind.stop(function(err, result) { + node.stop(function(err, result) { + if(err) { + throw err; + } done(); }); }); diff --git a/integration/regtest.js b/integration/regtest.js index 6e16ad0d..ae804d8a 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -61,9 +61,13 @@ describe('Daemon Binding Functionality', function() { throw err; } - bitcoind = require('../').daemon({ - datadir: datadir, - network: 'regtest' + bitcoind = require('../').modules.BitcoinModule({ + node: { + datadir: datadir, + network: { + name: 'regtest' + } + } }); bitcoind.on('error', function(err) { diff --git a/lib/chain.js b/lib/chain.js index 66a47777..ceb54e66 100644 --- a/lib/chain.js +++ b/lib/chain.js @@ -90,7 +90,7 @@ Chain.prototype._onInitialized = function() { }; Chain.prototype.start = function(callback) { - this.genesis = Block.fromBuffer(this.node.bitcoind.genesisBuffer); + this.genesis = Block.fromBuffer(this.node.modules.bitcoind.genesisBuffer); this.once('initialized', callback); this.initialize(); }; @@ -152,7 +152,7 @@ Chain.prototype.startBuilder = function() { Chain.prototype.getWeight = function getWeight(blockHash, callback) { var self = this; - var blockIndex = self.node.bitcoind.getBlockIndex(blockHash); + var blockIndex = self.node.modules.bitcoind.getBlockIndex(blockHash); setImmediate(function() { if (blockIndex) { diff --git a/lib/daemon.js b/lib/daemon.js deleted file mode 100644 index 297d02ac..00000000 --- a/lib/daemon.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var bitcoind = require('bindings')('bitcoind.node'); -var index = require('./'); -var log = index.log; -var bitcore = require('bitcore'); -var $ = bitcore.util.preconditions; - -function Daemon(options) { - var self = this; - - if (!(this instanceof Daemon)) { - return new Daemon(options); - } - - if (Object.keys(this.instances).length) { - throw new Error('Daemon cannot be instantiated more than once.'); - } - - EventEmitter.call(this); - - $.checkArgument(options.datadir, 'Please specify a datadir'); - - this.options = options || {}; - this.options.datadir = this.options.datadir.replace(/^~/, process.env.HOME); - this.datadir = this.options.datadir; - - this.node = options.node; - - this.config = this.datadir + '/bitcoin.conf'; - - Object.keys(exports).forEach(function(key) { - self[key] = exports[key]; - }); - -} - -util.inherits(Daemon, EventEmitter); - -Daemon.instances = {}; -Daemon.prototype.instances = Daemon.instances; - -Daemon.__defineGetter__('global', function() { - return Daemon.instances[Object.keys(Daemon.instances)[0]]; -}); - -Daemon.prototype.__defineGetter__('global', function() { - return Daemon.global; -}); - -Daemon.prototype.start = function(callback) { - var self = this; - - if (this.instances[this.datadir]) { - return callback(new Error('Daemon already started')); - } - this.instances[this.datadir] = true; - - bitcoind.start(this.options, function(err) { - if(err) { - return callback(err); - } - - self._started = true; - - bitcoind.onBlocksReady(function(err, result) { - - function onTipUpdateListener(result) { - if (result) { - // Emit and event that the tip was updated - self.height = result; - self.emit('tip', result); - // Recursively wait until the next update - bitcoind.onTipUpdate(onTipUpdateListener); - } - } - - bitcoind.onTipUpdate(onTipUpdateListener); - - bitcoind.startTxMon(function(txs) { - for(var i = 0; i < txs.length; i++) { - self.emit('tx', txs[i]); - } - }); - - // Set the current chain height - var info = self.getInfo(); - self.height = info.blocks; - - // Get the genesis block - self.getBlock(0, function(err, block) { - self.genesisBuffer = block; - self.emit('ready', result); - setImmediate(callback); - }); - - }); - }); -}; - -Daemon.prototype.isSynced = function() { - return bitcoind.isSynced(); -}; - -Daemon.prototype.syncPercentage = function() { - return bitcoind.syncPercentage(); -}; - -Daemon.prototype.getBlock = function(blockhash, callback) { - return bitcoind.getBlock(blockhash, callback); -}; - -Daemon.prototype.isSpent = function(txid, outputIndex) { - return bitcoind.isSpent(txid, outputIndex); -}; - -Daemon.prototype.getBlockIndex = function(blockHash) { - return bitcoind.getBlockIndex(blockHash); -}; - -Daemon.prototype.estimateFee = function(blocks) { - return bitcoind.estimateFee(blocks); -}; - -Daemon.prototype.sendTransaction = function(transaction, allowAbsurdFees) { - return bitcoind.sendTransaction(transaction, allowAbsurdFees); -}; - -Daemon.prototype.getTransaction = function(txid, queryMempool, callback) { - return bitcoind.getTransaction(txid, queryMempool, callback); -}; - -Daemon.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) { - return bitcoind.getTransactionWithBlockInfo(txid, queryMempool, callback); -}; - -Daemon.prototype.getMempoolOutputs = function(address) { - return bitcoind.getMempoolOutputs(address); -}; - -Daemon.prototype.addMempoolUncheckedTransaction = function(txBuffer) { - return bitcoind.addMempoolUncheckedTransaction(txBuffer); -}; - -Daemon.prototype.getInfo = function() { - return bitcoind.getInfo(); -}; - -Daemon.prototype.stop = function(callback) { - var self = this; - return bitcoind.stop(function(err, status) { - setImmediate(function() { - if (err) { - return callback(err); - } else { - log.info(status); - return callback(); - } - }); - }); -}; - -module.exports = Daemon; diff --git a/lib/db.js b/lib/db.js index 99bdb43e..b6f3d3a2 100644 --- a/lib/db.js +++ b/lib/db.js @@ -71,7 +71,7 @@ DB.prototype.initialize = function() { }; DB.prototype.start = function(callback) { - this.node.bitcoind.on('tx', this.transactionHandler.bind(this)); + this.node.modules.bitcoind.on('tx', this.transactionHandler.bind(this)); this.emit('ready'); setImmediate(callback); }; @@ -93,7 +93,7 @@ DB.prototype.getBlock = function(hash, callback) { var self = this; // get block from bitcoind - this.node.bitcoind.getBlock(hash, function(err, blockData) { + this.node.modules.bitcoind.getBlock(hash, function(err, blockData) { if(err) { return callback(err); } @@ -102,7 +102,7 @@ DB.prototype.getBlock = function(hash, callback) { }; DB.prototype.getPrevHash = function(blockHash, callback) { - var blockIndex = this.node.bitcoind.getBlockIndex(blockHash); + var blockIndex = this.node.modules.bitcoind.getBlockIndex(blockHash); setImmediate(function() { if (blockIndex) { callback(null, blockIndex.prevHash); @@ -118,7 +118,7 @@ DB.prototype.putBlock = function(block, callback) { }; DB.prototype.getTransaction = function(txid, queryMempool, callback) { - this.node.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) { + this.node.modules.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) { if(err) { return callback(err); } @@ -131,7 +131,7 @@ DB.prototype.getTransaction = function(txid, queryMempool, callback) { }; DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) { - this.node.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) { + this.node.modules.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) { if(err) { return callback(err); } @@ -151,7 +151,7 @@ DB.prototype.sendTransaction = function(tx, callback) { $.checkArgument(typeof tx === 'string', 'Argument must be a hex string or Transaction'); try { - var txid = this.node.bitcoind.sendTransaction(tx); + var txid = this.node.modules.bitcoind.sendTransaction(tx); return callback(null, txid); } catch(err) { return callback(err); @@ -162,7 +162,7 @@ DB.prototype.estimateFee = function(blocks, callback) { var self = this; setImmediate(function() { - callback(null, self.node.bitcoind.estimateFee(blocks)); + callback(null, self.node.modules.bitcoind.estimateFee(blocks)); }); }; @@ -278,8 +278,9 @@ DB.prototype.blockHandler = function(block, add, callback) { if(err) { return next(err); } - - operations = operations.concat(ops); + if (ops) { + operations = operations.concat(ops); + } next(); }); }, diff --git a/lib/module.js b/lib/module.js index bf713264..7b7fd19c 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,9 +1,16 @@ 'use strict'; +var util = require('util'); +var EventEmitter = require('events').EventEmitter; + var Module = function(options) { + EventEmitter.call(this); + this.node = options.node; }; +util.inherits(Module, EventEmitter); + /** * Describes the dependencies that should be loaded before this module. */ diff --git a/lib/modules/address.js b/lib/modules/address.js index b09b5af3..36e0f738 100644 --- a/lib/modules/address.js +++ b/lib/modules/address.js @@ -21,7 +21,7 @@ var AddressModule = function(options) { this.subscriptions['address/transaction'] = {}; this.subscriptions['address/balance'] = {}; - this.node.bitcoind.on('tx', this.transactionHandler.bind(this)); + this.node.modules.bitcoind.on('tx', this.transactionHandler.bind(this)); }; @@ -368,7 +368,7 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback } if(queryMempool) { - outputs = outputs.concat(self.node.bitcoind.getMempoolOutputs(addressStr)); + outputs = outputs.concat(self.node.modules.bitcoind.getMempoolOutputs(addressStr)); } callback(null, outputs); @@ -435,7 +435,7 @@ AddressModule.prototype.isSpent = function(output, queryMempool, callback) { var txid = output.prevTxId ? output.prevTxId.toString('hex') : output.txid; setImmediate(function() { - callback(self.node.bitcoind.isSpent(txid, output.outputIndex)); + callback(self.node.modules.bitcoind.isSpent(txid, output.outputIndex)); }); }; diff --git a/lib/modules/bitcoind.js b/lib/modules/bitcoind.js new file mode 100644 index 00000000..433c8910 --- /dev/null +++ b/lib/modules/bitcoind.js @@ -0,0 +1,220 @@ +'use strict'; + +var util = require('util'); +var Module = require('../module'); +var bindings = require('bindings')('bitcoind.node'); +var mkdirp = require('mkdirp'); +var fs = require('fs'); +var index = require('../'); +var log = index.log; +var bitcore = require('bitcore'); +var $ = bitcore.util.preconditions; + +/** + * Provides an interface to native bindings to Bitcoin Core + * @param {Object} options + * @param {String} options.datadir - The bitcoin data directory + * @param {Node} options.node - A reference to the node + */ +function Bitcoin(options) { + if (!(this instanceof Bitcoin)) { + return new Bitcoin(options); + } + + var self = this; + + Module.call(this, options); + + if (Object.keys(this.instances).length) { + throw new Error('Bitcoin cannot be instantiated more than once.'); + } + + $.checkState(this.node.datadir, 'Node is missing datadir property'); + + Object.keys(exports).forEach(function(key) { + self[key] = exports[key]; + }); + +} + +util.inherits(Bitcoin, Module); + +Bitcoin.dependencies = []; + +Bitcoin.instances = {}; +Bitcoin.prototype.instances = Bitcoin.instances; + +Bitcoin.__defineGetter__('global', function() { + return Bitcoin.instances[Object.keys(Bitcoin.instances)[0]]; +}); + +Bitcoin.prototype.__defineGetter__('global', function() { + return Bitcoin.global; +}); + +Bitcoin.DEFAULT_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n'; + +Bitcoin.prototype._loadConfiguration = function() { + /* jshint maxstatements: 25 */ + + $.checkArgument(this.node.datadir, 'Please specify "datadir" in configuration options'); + var configPath = this.node.datadir + '/bitcoin.conf'; + this.configuration = {}; + + if (!fs.existsSync(this.node.datadir)) { + mkdirp.sync(this.node.datadir); + } + + if (!fs.existsSync(configPath)) { + fs.writeFileSync(configPath, Bitcoin.DEFAULT_CONFIG); + } + + var file = fs.readFileSync(configPath); + var unparsed = file.toString().split('\n'); + for(var i = 0; i < unparsed.length; i++) { + var line = unparsed[i]; + if (!line.match(/^\#/) && line.match(/\=/)) { + var option = line.split('='); + var value; + if (!Number.isNaN(Number(option[1]))) { + value = Number(option[1]); + } else { + value = option[1]; + } + this.configuration[option[0]] = value; + } + } + + $.checkState( + this.configuration.txindex && this.configuration.txindex === 1, + 'Txindex option is required in order to use most of the features of bitcore-node. ' + + 'Please add "txindex=1" to your configuration and reindex an existing database if ' + + 'necessary with reindex=1' + ); +}; + +Bitcoin.prototype.start = function(callback) { + var self = this; + + this._loadConfiguration(); + + if (this.instances[this.datadir]) { + return callback(new Error('Bitcoin already started')); + } + this.instances[this.datadir] = true; + + bindings.start({ + datadir: this.node.datadir, + network: this.node.network.name + }, function(err) { + if(err) { + return callback(err); + } + + self._started = true; + + bindings.onBlocksReady(function(err, result) { + + function onTipUpdateListener(result) { + if (result) { + // Emit and event that the tip was updated + self.height = result; + self.emit('tip', result); + + // TODO stopping status + if(!self.stopping) { + var percentage = self.syncPercentage(); + log.info('Bitcoin Core Daemon New Height:', self.height, 'Percentage:', percentage); + } + + // Recursively wait until the next update + bindings.onTipUpdate(onTipUpdateListener); + } + } + + bindings.onTipUpdate(onTipUpdateListener); + + bindings.startTxMon(function(txs) { + for(var i = 0; i < txs.length; i++) { + self.emit('tx', txs[i]); + } + }); + + // Set the current chain height + var info = self.getInfo(); + self.height = info.blocks; + + // Get the genesis block + self.getBlock(0, function(err, block) { + self.genesisBuffer = block; + self.emit('ready', result); + log.info('Bitcoin Daemon Ready'); + setImmediate(callback); + }); + + }); + }); +}; + +Bitcoin.prototype.isSynced = function() { + return bindings.isSynced(); +}; + +Bitcoin.prototype.syncPercentage = function() { + return bindings.syncPercentage(); +}; + +Bitcoin.prototype.getBlock = function(blockhash, callback) { + return bindings.getBlock(blockhash, callback); +}; + +Bitcoin.prototype.isSpent = function(txid, outputIndex) { + return bindings.isSpent(txid, outputIndex); +}; + +Bitcoin.prototype.getBlockIndex = function(blockHash) { + return bindings.getBlockIndex(blockHash); +}; + +Bitcoin.prototype.estimateFee = function(blocks) { + return bindings.estimateFee(blocks); +}; + +Bitcoin.prototype.sendTransaction = function(transaction, allowAbsurdFees) { + return bindings.sendTransaction(transaction, allowAbsurdFees); +}; + +Bitcoin.prototype.getTransaction = function(txid, queryMempool, callback) { + return bindings.getTransaction(txid, queryMempool, callback); +}; + +Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) { + return bindings.getTransactionWithBlockInfo(txid, queryMempool, callback); +}; + +Bitcoin.prototype.getMempoolOutputs = function(address) { + return bindings.getMempoolOutputs(address); +}; + +Bitcoin.prototype.addMempoolUncheckedTransaction = function(txBuffer) { + return bindings.addMempoolUncheckedTransaction(txBuffer); +}; + +Bitcoin.prototype.getInfo = function() { + return bindings.getInfo(); +}; + +Bitcoin.prototype.stop = function(callback) { + return bindings.stop(function(err, status) { + setImmediate(function() { + if (err) { + return callback(err); + } else { + log.info(status); + return callback(); + } + }); + }); +}; + +module.exports = Bitcoin; diff --git a/lib/node.js b/lib/node.js index cc6fa3d5..1bba402d 100644 --- a/lib/node.js +++ b/lib/node.js @@ -15,7 +15,6 @@ var Chain = require('./chain'); var DB = require('./db'); var index = require('./'); var log = index.log; -var daemon = require('./daemon'); var Bus = require('./bus'); var BaseModule = require('./module'); @@ -37,6 +36,9 @@ function Node(config) { this._unloadedModules = config.modules; } + $.checkState(config.datadir, 'Node config expects "datadir"'); + this.datadir = config.datadir; + this._loadConfiguration(config); this._initialize(); } @@ -97,61 +99,12 @@ Node.prototype.getAllPublishEvents = function() { }; Node.prototype._loadConfiguration = function(config) { - this._loadBitcoinConf(config); - this._loadBitcoind(config); this._loadNetwork(config); this._loadDB(config); this._loadAPI(); this._loadConsensus(config); }; -Node.DEFAULT_DAEMON_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n'; - -Node.prototype._loadBitcoinConf = function(config) { - $.checkArgument(config.datadir, 'Please specify "datadir" in configuration options'); - var configPath = config.datadir + '/bitcoin.conf'; - this.bitcoinConfiguration = {}; - - if (!fs.existsSync(config.datadir)) { - mkdirp.sync(config.datadir); - } - - if (!fs.existsSync(configPath)) { - fs.writeFileSync(configPath, Node.DEFAULT_DAEMON_CONFIG); - } - - var file = fs.readFileSync(configPath); - var unparsed = file.toString().split('\n'); - for(var i = 0; i < unparsed.length; i++) { - var line = unparsed[i]; - if (!line.match(/^\#/) && line.match(/\=/)) { - var option = line.split('='); - var value; - if (!Number.isNaN(Number(option[1]))) { - value = Number(option[1]); - } else { - value = option[1]; - } - this.bitcoinConfiguration[option[0]] = value; - } - } - - $.checkState((this.bitcoinConfiguration.txindex && this.bitcoinConfiguration.txindex == 1), - 'Txindex option is required in order to use most of the features of bitcore-node. \ -Please add "txindex=1" to your configuration and reindex an existing database if necessary with reindex=1'); -}; - -Node.prototype._loadBitcoind = function(config) { - var bitcoindConfig = {}; - bitcoindConfig.datadir = config.datadir; - bitcoindConfig.network = config.network; - bitcoindConfig.node = this; - - // start the bitcoind daemon - this.bitcoind = daemon(bitcoindConfig); - -}; - /** * This function will find the common ancestor between the current chain and a forked block, * by moving backwards from the forked block until it meets the current chain. @@ -182,7 +135,7 @@ Node.prototype._syncBitcoindAncestor = function(block, done) { // and thus don't need to find the entire chain of hashes. while(ancestorHash && !currentHashesMap[ancestorHash]) { - var blockIndex = self.bitcoind.getBlockIndex(ancestorHash); + var blockIndex = self.modules.bitcoind.getBlockIndex(ancestorHash); ancestorHash = blockIndex ? blockIndex.prevHash : null; } @@ -276,9 +229,9 @@ Node.prototype._syncBitcoind = function() { async.whilst(function() { height = self.chain.tip.__height; - return height < self.bitcoind.height && !self.stopping; + return height < self.modules.bitcoind.height && !self.stopping; }, function(done) { - self.bitcoind.getBlock(height + 1, function(err, blockBuffer) { + self.modules.bitcoind.getBlock(height + 1, function(err, blockBuffer) { if (err) { return done(err); } @@ -340,7 +293,7 @@ Node.prototype._syncBitcoind = function() { self.chain.lastSavedMetadataThreshold = 0; // If bitcoind is completely synced - if (self.bitcoind.isSynced()) { + if (self.modules.bitcoind.isSynced()) { self.emit('synced'); } @@ -433,7 +386,6 @@ Node.prototype._loadAPI = function() { Node.prototype._initialize = function() { var self = this; - this._initializeBitcoind(); this._initializeDatabase(); this._initializeChain(); @@ -445,30 +397,6 @@ Node.prototype._initialize = function() { }); }; -Node.prototype._initializeBitcoind = function() { - var self = this; - - // Notify that there is a new tip - this.bitcoind.on('ready', function() { - log.info('Bitcoin Daemon Ready'); - }); - - // Notify that there is a new tip - this.bitcoind.on('tip', function(height) { - if(!self.stopping) { - var percentage = self.bitcoind.syncPercentage(); - log.info('Bitcoin Core Daemon New Height:', height, 'Percentage:', percentage); - self._syncBitcoind(); - } - }); - - this.bitcoind.on('error', function(err) { - Error.captureStackTrace(err); - self.emit('error', err); - }); - -}; - Node.prototype._initializeDatabase = function() { var self = this; this.db.on('ready', function() { @@ -482,10 +410,20 @@ Node.prototype._initializeDatabase = function() { }; Node.prototype._initializeChain = function() { + var self = this; this.chain.on('ready', function() { log.info('Bitcoin Chain Ready'); - self._syncBitcoind(); + + // Notify that there is a new tip + self.modules.bitcoind.on('tip', function(height) { + if(!self.stopping) { + var percentage = self.modules.bitcoind.syncPercentage(); + log.info('Bitcoin Core Daemon New Height:', height, 'Percentage:', percentage); + self._syncBitcoind(); + } + }); + }); this.chain.on('error', function(err) { Error.captureStackTrace(err); @@ -495,10 +433,6 @@ Node.prototype._initializeChain = function() { Node.prototype.getServices = function() { var services = [ - { - name: 'bitcoind', - dependencies: [] - }, { name: 'db', dependencies: ['bitcoind'], @@ -535,6 +469,7 @@ Node.prototype.getServiceOrder = function() { var name = names[i]; var service = servicesByName[name]; + $.checkState(service, 'Required dependency "' + name + '" not available.'); // first add the dependencies addToStack(service.dependencies); @@ -586,7 +521,6 @@ Node.prototype.stop = function(callback) { services, function(service, next) { log.info('Stopping ' + service.name); - if (service.module) { self.modules[service.name].stop(next); } else { diff --git a/lib/scaffold/default-config.js b/lib/scaffold/default-config.js index 81a7e2ba..59a5e48e 100644 --- a/lib/scaffold/default-config.js +++ b/lib/scaffold/default-config.js @@ -13,7 +13,7 @@ function getDefaultConfig() { datadir: process.env.BITCORENODE_DIR || path.resolve(process.env.HOME, '.bitcoin'), network: process.env.BITCORENODE_NETWORK || 'livenet', port: process.env.BITCORENODE_PORT || 3001, - modules: ['address'] + modules: ['bitcoind', 'address'] } }; } diff --git a/test/chain.unit.js b/test/chain.unit.js index 6e7e30c1..185aaa66 100644 --- a/test/chain.unit.js +++ b/test/chain.unit.js @@ -30,8 +30,9 @@ describe('Bitcoin Chain', function() { it('should call the callback when base chain is initialized', function(done) { var chain = new Chain(); chain.node = {}; - chain.node.bitcoind = {}; - chain.node.bitcoind.genesisBuffer = new Buffer('0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000', 'hex'); + chain.node.modules = {}; + chain.node.modules.bitcoind = {}; + chain.node.modules.bitcoind.genesisBuffer = new Buffer('0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000', 'hex'); chain.initialize = function() { chain.emit('initialized'); }; @@ -196,7 +197,8 @@ describe('Bitcoin Chain', function() { var chain = new Chain(); chain.node = {}; chain.node.db = {}; - chain.node.bitcoind = { + chain.node.modules = {}; + chain.node.modules.bitcoind = { getBlockIndex: sinon.stub().returns({ chainWork: work }) @@ -211,7 +213,7 @@ describe('Bitcoin Chain', function() { }); it('should give an error if the weight is undefined', function(done) { - chain.node.bitcoind.getBlockIndex = sinon.stub().returns(undefined); + chain.node.modules.bitcoind.getBlockIndex = sinon.stub().returns(undefined); chain.getWeight('hash2', function(err, weight) { should.exist(err); done(); diff --git a/test/db.unit.js b/test/db.unit.js index a7a7c4c8..7d2a5b5b 100644 --- a/test/db.unit.js +++ b/test/db.unit.js @@ -10,8 +10,6 @@ var Block = bitcore.Block; var transactionData = require('./data/bitcoin-transactions.json'); var errors = index.errors; var memdown = require('memdown'); -var inherits = require('util').inherits; -var BaseModule = require('../lib/module'); var bitcore = require('bitcore'); var Transaction = bitcore.Transaction; @@ -21,7 +19,8 @@ describe('Bitcoin DB', function() { it('should emit ready', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { on: sinon.spy() }; db.addModule = sinon.spy(); @@ -51,7 +50,8 @@ describe('Bitcoin DB', function() { it('will return a NotFound error', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getTransaction: sinon.stub().callsArgWith(2, null, null) }; var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'; @@ -63,7 +63,8 @@ describe('Bitcoin DB', function() { it('will return an error from bitcoind', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getTransaction: sinon.stub().callsArgWith(2, new Error('test error')) }; var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'; @@ -75,7 +76,8 @@ describe('Bitcoin DB', function() { it('will return an error from bitcoind', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getTransaction: sinon.stub().callsArgWith(2, null, new Buffer(transactionData[0].hex, 'hex')) }; var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'; @@ -94,7 +96,8 @@ describe('Bitcoin DB', function() { var blockBuffer = new Buffer(blockData, 'hex'); var expectedBlock = Block.fromBuffer(blockBuffer); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getBlock: sinon.stub().callsArgWith(1, null, blockBuffer) }; @@ -107,8 +110,9 @@ describe('Bitcoin DB', function() { }); it('should give an error when bitcoind.js gives an error', function(done) { db.node = {}; - db.node.bitcoind = {}; - db.node.bitcoind.getBlock = sinon.stub().callsArgWith(1, new Error('error')); + db.node.modules = {}; + db.node.modules.bitcoind = {}; + db.node.modules.bitcoind.getBlock = sinon.stub().callsArgWith(1, new Error('error')); db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) { should.exist(err); err.message.should.equal('error'); @@ -131,7 +135,8 @@ describe('Bitcoin DB', function() { it('should return prevHash from bitcoind', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getBlockIndex: sinon.stub().returns({ prevHash: 'prevhash' }) @@ -147,7 +152,8 @@ describe('Bitcoin DB', function() { it('should give an error if bitcoind could not find it', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getBlockIndex: sinon.stub().returns(null) }; @@ -169,7 +175,8 @@ describe('Bitcoin DB', function() { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, info) }; @@ -183,7 +190,8 @@ describe('Bitcoin DB', function() { it('should give an error if one occurred', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, new Error('error')) }; @@ -198,7 +206,8 @@ describe('Bitcoin DB', function() { it('should give the txid on success', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { sendTransaction: sinon.stub().returns('txid') }; @@ -212,7 +221,8 @@ describe('Bitcoin DB', function() { it('should give an error if bitcoind threw an error', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { sendTransaction: sinon.stub().throws(new Error('error')) }; @@ -228,14 +238,15 @@ describe('Bitcoin DB', function() { it('should pass along the fee from bitcoind', function(done) { var db = new DB({store: memdown}); db.node = {}; - db.node.bitcoind = { + db.node.modules = {}; + db.node.modules.bitcoind = { estimateFee: sinon.stub().returns(1000) }; db.estimateFee(5, function(err, fee) { should.not.exist(err); fee.should.equal(1000); - db.node.bitcoind.estimateFee.args[0][0].should.equal(5); + db.node.modules.bitcoind.estimateFee.args[0][0].should.equal(5); done(); }); }); diff --git a/test/modules/address.unit.js b/test/modules/address.unit.js index 6031de9b..b8ae8d33 100644 --- a/test/modules/address.unit.js +++ b/test/modules/address.unit.js @@ -11,15 +11,14 @@ var errors = bitcorenode.errors; var levelup = require('levelup'); var mockdb = { - bitcoind: { - on: sinon.stub() - } }; var mocknode = { db: mockdb, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; @@ -103,12 +102,6 @@ describe('AddressModule', function() { describe('#blockHandler', function() { var am; - var db = { - bitcoind: { - on: sinon.stub() - } - }; - var testBlock = bitcore.Block.fromString(blockData); var data = [ @@ -212,12 +205,6 @@ describe('AddressModule', function() { }); }); it('should continue if output script is null', function(done) { - var db = { - bitcoind: { - on: sinon.stub() - } - }; - var am = new AddressModule({node: mocknode, network: 'livenet'}); var block = { @@ -247,15 +234,13 @@ describe('AddressModule', function() { }); it('will call event handlers', function() { var testBlock = bitcore.Block.fromString(blockData); - var db = { - bitcoind: { - on: sinon.stub() - } - }; + var db = {}; var testnode = { db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; var am = new AddressModule({node: testnode, network: 'livenet'}); @@ -444,8 +429,10 @@ describe('AddressModule', function() { } }, db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; @@ -467,7 +454,7 @@ describe('AddressModule', function() { blockHeight: 352532 } ]; - am.node.bitcoind = { + am.node.modules.bitcoind = { getMempoolOutputs: sinon.stub().returns(mempoolOutputs) }; @@ -535,15 +522,13 @@ describe('AddressModule', function() { 'addr3': ['utxo3'] }; - var db = { - bitcoind: { - on: sinon.spy() - } - }; + var db = {}; var testnode = { db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; var am = new AddressModule({node: testnode}); @@ -570,14 +555,18 @@ describe('AddressModule', function() { }; var db = { - bitcoind: { - on: sinon.spy() + modules: { + bitcoind: { + on: sinon.spy() + } } }; var testnode = { db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; var am = new AddressModule({node: testnode}); @@ -604,15 +593,13 @@ describe('AddressModule', function() { 'addr3': ['utxo3'] }; - var db = { - bitcoind: { - on: sinon.spy() - } - }; + var db = {}; var testnode = { db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; var am = new AddressModule({node: testnode}); @@ -721,20 +708,18 @@ describe('AddressModule', function() { describe('#isSpent', function() { var am; - var db = { - bitcoind: { - on: sinon.stub() - } - }; + var db = {}; var testnode = { db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; before(function() { am = new AddressModule({node: testnode}); - am.node.bitcoind = { + am.node.modules.bitcoind = { isSpent: sinon.stub().returns(true), on: sinon.stub() }; @@ -757,8 +742,10 @@ describe('AddressModule', function() { }; var testnode = { db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; var am = new AddressModule({node: testnode}); @@ -875,8 +862,10 @@ describe('AddressModule', function() { } }, db: db, - bitcoind: { - on: sinon.stub() + modules: { + bitcoind: { + on: sinon.stub() + } } }; var am = new AddressModule({node: testnode}); diff --git a/test/modules/bitcoind.unit.js b/test/modules/bitcoind.unit.js new file mode 100644 index 00000000..8dc099b2 --- /dev/null +++ b/test/modules/bitcoind.unit.js @@ -0,0 +1,50 @@ +'use strict'; + +var should = require('chai').should(); +var proxyquire = require('proxyquire'); +var fs = require('fs'); +var sinon = require('sinon'); +var BitcoinModule = proxyquire('../../lib/modules/bitcoind', { + fs: { + readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/../data/bitcoin.conf')) + } +}); +var BadBitcoin = proxyquire('../../lib/modules/bitcoind', { + fs: { + readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/../data/badbitcoin.conf')) + } +}); + +describe('Bitcoin Module', function() { + var baseConfig = { + node: { + datadir: 'testdir', + network: { + name: 'regtest' + } + } + }; + describe('#_loadConfiguration', function() { + it('will parse a bitcoin.conf file', function() { + var bitcoind = new BitcoinModule(baseConfig); + bitcoind._loadConfiguration({datadir: process.env.HOME + '/.bitcoin'}); + should.exist(bitcoind.configuration); + bitcoind.configuration.should.deep.equal({ + server: 1, + whitelist: '127.0.0.1', + txindex: 1, + port: 20000, + rpcallowip: '127.0.0.1', + rpcuser: 'bitcoin', + rpcpassword: 'local321' + }); + }); + it('should throw an exception if txindex isn\'t enabled in the configuration', function() { + var bitcoind = new BadBitcoin(baseConfig); + (function() { + bitcoind._loadConfiguration({datadir: './test'}); + }).should.throw('Txindex option'); + }); + }); +}); + diff --git a/test/node.unit.js b/test/node.unit.js index 3f07475e..78672ae6 100644 --- a/test/node.unit.js +++ b/test/node.unit.js @@ -11,15 +11,17 @@ var blockData = require('./data/livenet-345003.json'); var proxyquire = require('proxyquire'); var index = require('..'); var fs = require('fs'); -var bitcoinConfBuffer = fs.readFileSync(__dirname + '/data/bitcoin.conf'); var chainHashes = require('./data/hashes.json'); var util = require('util'); var BaseModule = require('../lib/module'); describe('Bitcore Node', function() { + var baseConfig = { + datadir: 'testdir' + }; + var Node; - var BadNode; function hexlebuf(hexString){ return BufferUtil.reverse(new Buffer(hexString, 'hex')); @@ -30,23 +32,9 @@ describe('Bitcore Node', function() { } before(function() { - - BadNode = proxyquire('../lib/node', { - fs: { - readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/data/badbitcoin.conf')) - } - }); - BadNode.prototype._loadConfiguration = sinon.spy(); - BadNode.prototype._initialize = sinon.spy(); - - Node = proxyquire('../lib/node', { - fs: { - readFileSync: sinon.stub().returns(bitcoinConfBuffer) - } - }); + Node = proxyquire('../lib/node', {}); Node.prototype._loadConfiguration = sinon.spy(); Node.prototype._initialize = sinon.spy(); - }); describe('@constructor', function() { @@ -60,6 +48,7 @@ describe('Bitcore Node', function() { ]; }; var config = { + datadir: 'testdir', modules: [ { name: 'test1', @@ -81,7 +70,7 @@ describe('Bitcore Node', function() { describe('#openBus', function() { it('will create a new bus', function() { - var node = new Node({}); + var node = new Node(baseConfig); var bus = node.openBus(); bus.node.should.equal(node); }); @@ -89,7 +78,7 @@ describe('Bitcore Node', function() { describe('#addModule', function() { it('will instantiate an instance and load api methods', function() { - var node = new Node({}); + var node = new Node(baseConfig); function TestModule() {} util.inherits(TestModule, BaseModule); TestModule.prototype.getData = function() {}; @@ -110,7 +99,7 @@ describe('Bitcore Node', function() { describe('#getAllAPIMethods', function() { it('should return db methods and modules methods', function() { - var node = new Node({}); + var node = new Node(baseConfig); node.modules = { module1: { getAPIMethods: sinon.stub().returns(['mda1', 'mda2']) @@ -130,7 +119,7 @@ describe('Bitcore Node', function() { }); describe('#getAllPublishEvents', function() { it('should return modules publish events', function() { - var node = new Node({}); + var node = new Node(baseConfig); node.modules = { module1: { getPublishEvents: sinon.stub().returns(['mda1', 'mda2']) @@ -150,62 +139,20 @@ describe('Bitcore Node', function() { }); describe('#_loadConfiguration', function() { it('should call the necessary methods', function() { - var TestNode = proxyquire('../lib/node', { - fs: { - readFileSync: sinon.stub().returns(bitcoinConfBuffer) - } - }); + var TestNode = proxyquire('../lib/node', {}); TestNode.prototype._initialize = sinon.spy(); - TestNode.prototype._loadBitcoinConf = sinon.spy(); - TestNode.prototype._loadBitcoind = sinon.spy(); TestNode.prototype._loadDB = sinon.spy(); TestNode.prototype._loadAPI = sinon.spy(); TestNode.prototype._loadConsensus = sinon.spy(); - var node = new TestNode({}); - node._loadBitcoind.callCount.should.equal(1); - node._loadBitcoinConf.callCount.should.equal(1); + var node = new TestNode(baseConfig); node._loadDB.callCount.should.equal(1); node._loadAPI.callCount.should.equal(1); node._loadConsensus.callCount.should.equal(1); }); }); - describe('#_loadBitcoinConf', function() { - it('will parse a bitcoin.conf file', function() { - var node = new Node({}); - node._loadBitcoinConf({datadir: process.env.HOME + '/.bitcoin'}); - should.exist(node.bitcoinConfiguration); - node.bitcoinConfiguration.should.deep.equal({ - server: 1, - whitelist: '127.0.0.1', - txindex: 1, - port: 20000, - rpcallowip: '127.0.0.1', - rpcuser: 'bitcoin', - rpcpassword: 'local321' - }); - }); - }); - describe('#_loadBitcoind', function() { - it('should initialize', function() { - var node = new Node({}); - node._loadBitcoind({datadir: './test'}); - should.exist(node.bitcoind); - }); - it('should initialize with testnet', function() { - var node = new Node({}); - node._loadBitcoind({datadir: './test', testnet: true}); - should.exist(node.bitcoind); - }); - it('should throw an exception if txindex isn\'t enabled in the configuration', function() { - var node = new BadNode({}); - (function() { - node._loadBitcoinConf({datadir: './test'}); - }).should.throw('Txindex option'); - }); - }); describe('#_syncBitcoindAncestor', function() { it('will find an ancestor 6 deep', function() { - var node = new Node({}); + var node = new Node(baseConfig); node.chain = { getHashes: function(tipHash, callback) { callback(null, chainHashes); @@ -248,7 +195,8 @@ describe('Bitcore Node', function() { } }, }; - node.bitcoind = { + node.modules = {}; + node.modules.bitcoind = { getBlockIndex: function(hash) { var block = forkedBlocks[hash]; return { @@ -267,7 +215,7 @@ describe('Bitcore Node', function() { }); describe('#_syncBitcoindRewind', function() { it('will undo blocks 6 deep', function() { - var node = new Node({}); + var node = new Node(baseConfig); var ancestorHash = chainHashes[chainHashes.length - 6]; node.chain = { tip: { @@ -317,10 +265,11 @@ describe('Bitcore Node', function() { }); describe('#_syncBitcoind', function() { it('will get and add block up to the tip height', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); var blockBuffer = new Buffer(blockData, 'hex'); var block = Block.fromBuffer(blockBuffer); - node.bitcoind = { + node.modules = {}; + node.modules.bitcoind = { getBlock: sinon.stub().callsArgWith(1, null, blockBuffer), isSynced: sinon.stub().returns(true), height: 1 @@ -349,8 +298,9 @@ describe('Bitcore Node', function() { node._syncBitcoind(); }); it('will exit and emit error with error from bitcoind.getBlock', function(done) { - var node = new Node({}); - node.bitcoind = { + var node = new Node(baseConfig); + node.modules = {}; + node.modules.bitcoind = { getBlock: sinon.stub().callsArgWith(1, new Error('test error')), height: 1 }; @@ -366,10 +316,11 @@ describe('Bitcore Node', function() { node._syncBitcoind(); }); it('will stop syncing when the node is stopping', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); var blockBuffer = new Buffer(blockData, 'hex'); var block = Block.fromBuffer(blockBuffer); - node.bitcoind = { + node.modules = {}; + node.modules.bitcoind = { getBlock: sinon.stub().callsArgWith(1, null, blockBuffer), isSynced: sinon.stub().returns(true), height: 1 @@ -411,6 +362,7 @@ describe('Bitcore Node', function() { describe('#_loadNetwork', function() { it('should use the testnet network if testnet is specified', function() { var config = { + datadir: 'testdir', network: 'testnet' }; var node = new Node(config); @@ -419,6 +371,7 @@ describe('Bitcore Node', function() { }); it('should use the regtest network if regtest is specified', function() { var config = { + datadir: 'testdir', network: 'regtest' }; var node = new Node(config); @@ -426,7 +379,9 @@ describe('Bitcore Node', function() { node.network.name.should.equal('regtest'); }); it('should use the livenet network if nothing is specified', function() { - var config = {}; + var config = { + datadir: 'testdir' + }; var node = new Node(config); node._loadNetwork(config); node.network.name.should.equal('livenet'); @@ -526,7 +481,7 @@ describe('Bitcore Node', function() { var node; before(function() { - node = new Node({}); + node = new Node(baseConfig); }); it('will set properties', function() { @@ -541,11 +496,7 @@ describe('Bitcore Node', function() { var node; before(function() { - var TestNode = proxyquire('../lib/node', { - fs: { - readFileSync: sinon.stub().returns(bitcoinConfBuffer) - } - }); + var TestNode = proxyquire('../lib/node', {}); TestNode.prototype._loadConfiguration = sinon.spy(); TestNode.prototype._initializeBitcoind = sinon.spy(); TestNode.prototype._initializeDatabase = sinon.spy(); @@ -555,7 +506,7 @@ describe('Bitcore Node', function() { var _initialize = TestNode.prototype._initialize; TestNode.prototype._initialize = sinon.spy(); - node = new TestNode({}); + node = new TestNode(baseConfig); node.chain = { on: sinon.spy() }; @@ -581,7 +532,6 @@ describe('Bitcore Node', function() { node._initialize(); // event handlers - node._initializeBitcoind.callCount.should.equal(1); node._initializeDatabase.callCount.should.equal(1); node._initializeChain.callCount.should.equal(1); @@ -599,51 +549,9 @@ describe('Bitcore Node', function() { }); - describe('#_initalizeBitcoind', function() { - - it('will call emit an error from libbitcoind', function(done) { - var node = new Node({}); - node.bitcoind = new EventEmitter(); - node.on('error', function(err) { - should.exist(err); - err.message.should.equal('test error'); - done(); - }); - node._initializeBitcoind(); - node.bitcoind.emit('error', new Error('test error')); - }); - it('will call sync when there is a new tip', function(done) { - var node = new Node({}); - node.bitcoind = new EventEmitter(); - node.bitcoind.syncPercentage = sinon.spy(); - node._syncBitcoind = function() { - node.bitcoind.syncPercentage.callCount.should.equal(1); - done(); - }; - node._initializeBitcoind(); - node.bitcoind.emit('tip', 10); - }); - it('will not call sync when there is a new tip and shutting down', function(done) { - var node = new Node({}); - node.bitcoind = new EventEmitter(); - node._syncBitcoind = sinon.spy(); - node.bitcoind.syncPercentage = sinon.spy(); - node.stopping = true; - node.bitcoind.on('tip', function() { - setImmediate(function() { - node.bitcoind.syncPercentage.callCount.should.equal(0); - node._syncBitcoind.callCount.should.equal(0); - done(); - }); - }); - node._initializeBitcoind(); - node.bitcoind.emit('tip', 10); - }); - }); - describe('#_initializeDatabase', function() { it('will log on ready event', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); node.db = new EventEmitter(); sinon.stub(index.log, 'info'); node.db.on('ready', function() { @@ -657,7 +565,7 @@ describe('Bitcore Node', function() { node.db.emit('ready'); }); it('will call emit an error from db', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); node.db = new EventEmitter(); node.on('error', function(err) { should.exist(err); @@ -670,21 +578,42 @@ describe('Bitcore Node', function() { }); describe('#_initializeChain', function() { - it('will call _syncBitcoind on ready', function(done) { - var node = new Node({}); - node._syncBitcoind = sinon.spy(); + + it('will call sync when there is a new tip', function(done) { + var node = new Node(baseConfig); node.chain = new EventEmitter(); - node.chain.on('ready', function(err) { + node.modules = {}; + node.modules.bitcoind = new EventEmitter(); + node.modules.bitcoind.syncPercentage = sinon.spy(); + node._syncBitcoind = function() { + node.modules.bitcoind.syncPercentage.callCount.should.equal(1); + done(); + }; + node._initializeChain(); + node.chain.emit('ready'); + node.modules.bitcoind.emit('tip', 10); + }); + it('will not call sync when there is a new tip and shutting down', function(done) { + var node = new Node(baseConfig); + node.chain = new EventEmitter(); + node.modules = {}; + node.modules.bitcoind = new EventEmitter(); + node._syncBitcoind = sinon.spy(); + node.modules.bitcoind.syncPercentage = sinon.spy(); + node.stopping = true; + node.modules.bitcoind.on('tip', function() { setImmediate(function() { - node._syncBitcoind.callCount.should.equal(1); + node.modules.bitcoind.syncPercentage.callCount.should.equal(0); + node._syncBitcoind.callCount.should.equal(0); done(); }); }); node._initializeChain(); node.chain.emit('ready'); + node.modules.bitcoind.emit('tip', 10); }); it('will emit an error from the chain', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); node.chain = new EventEmitter(); node.on('error', function(err) { should.exist(err); @@ -698,7 +627,7 @@ describe('Bitcore Node', function() { describe('#getServiceOrder', function() { it('should return the services in the correct order', function() { - var node = new Node({}); + var node = new Node(baseConfig); node.getServices = function() { return [ { @@ -729,7 +658,7 @@ describe('Bitcore Node', function() { describe('#start', function() { it('will call start for each module', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); function TestModule() {} util.inherits(TestModule, BaseModule); TestModule.prototype.start = sinon.stub().callsArg(0); @@ -760,7 +689,7 @@ describe('Bitcore Node', function() { describe('#stop', function() { it('will call stop for each module', function(done) { - var node = new Node({}); + var node = new Node(baseConfig); function TestModule() {} util.inherits(TestModule, BaseModule); TestModule.prototype.stop = sinon.stub().callsArg(0);