commit
216830976f
25
README.md
25
README.md
@ -5,16 +5,31 @@ A Node.js module that adds a native interface to Bitcoin Core for querying infor
|
||||
|
||||
## Install
|
||||
|
||||
Here is how you can you install and start your node:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bitpay/bitcore-node.git
|
||||
cd bitcore-node
|
||||
npm install
|
||||
npm install -g bitcore-node@0.2.0-beta.4
|
||||
bitcore-node start
|
||||
```
|
||||
Note: For convenience, we distribute binaries for x86_64 Linux and x86_64 Mac OS X. Upon npm install, the binaries for your platform will be downloaded. This greatly speeds up the process of using this project. If you don't want to compile the project for yourself, then please skip down to "Example Usage" section for next steps. Please see detailed instructions below for complete build details and dependencies needed for installation if you choose to build the project from source.
|
||||
|
||||
Note: For your convenience, we distribute binaries for x86_64 Linux and x86_64 Mac OS X. Upon npm install, the binaries for your platform will be downloaded. If you want to compile the project yourself, then please see the [Build & Install](#build--install) for full detailed instructions to build the project from source.
|
||||
|
||||
## Configuration
|
||||
|
||||
Bitcore Node includes a Command Line Interface (CLI) for managing, configuring and interfacing with your Bitcore Node. At the minimum, your node can function with all of the features from Bitcoin Core running as a full node. However you can enable additional features to make your node more useful such as exposing new APIs, adding new indexes for addresses, running a block explorer and custom modules.
|
||||
|
||||
```bash
|
||||
bitcore-node create -d <bitcoin-data-dir> mynode "My Node"
|
||||
cd mynode
|
||||
bitcore-node add <module>
|
||||
bitcore-node add https://github.com/yourname/helloworld
|
||||
```
|
||||
|
||||
This will create a directory with configuration files for your node and install the necessary dependencies. If you're interested in developing a module, please see the [Module Development Guide](#modules).
|
||||
|
||||
## Build & Install
|
||||
|
||||
There are two main parts of the build, compiling Bitcoin Core as a static library and the Node.js bindings.
|
||||
This includes a detailed instructions for compiling. There are two main parts of the build, compiling Bitcoin Core as a static library and the Node.js bindings.
|
||||
|
||||
### Ubuntu 14.04 (Unix/Linux)
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ Ensure you've followed the instructions in the README.md for building the projec
|
||||
|
||||
When publishing to npm, the .gitignore file is used to exclude files from the npm publishing process. Be sure that the bitcore-node directory has only the directories and files that you would like to publish to npm. You might need to run the commands below on each platform that you intend to publish (e.g. Mac and Linux).
|
||||
|
||||
To make a release, bump the version of the package.json:
|
||||
To make a release, bump the `version` and `lastBuild` of the `package.json`:
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
@ -44,7 +44,7 @@ npm run upload
|
||||
npm publish
|
||||
```
|
||||
|
||||
And then update the version of the package.json for development (e.g. "0.3.2-dev"):
|
||||
And then update the `version` of the `package.json` for development (e.g. "0.3.2-dev"):
|
||||
|
||||
```bash
|
||||
git commit -a -m "Bump development version to <version>"
|
||||
|
||||
175
bin/start.js
175
bin/start.js
@ -1,175 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var BitcoinNode = require('..').Node;
|
||||
var chainlib = require('chainlib');
|
||||
var socketio = require('socket.io');
|
||||
var log = chainlib.log;
|
||||
log.debug = function() {};
|
||||
var start = require('../lib/scaffold/start');
|
||||
var defaultConfig = require('../lib/scaffold/default-config');
|
||||
|
||||
var configuration = {
|
||||
datadir: process.env.BITCORENODE_DIR || '~/.bitcoin',
|
||||
network: process.env.BITCORENODE_NETWORK || 'livenet',
|
||||
port: 3000
|
||||
};
|
||||
|
||||
var node = new BitcoinNode(configuration);
|
||||
|
||||
var count = 0;
|
||||
var interval = false;
|
||||
|
||||
function logSyncStatus() {
|
||||
log.info('Sync Status: Tip:', node.chain.tip.hash, 'Height:', node.chain.tip.__height, 'Rate:', count/10, 'blocks per second');
|
||||
}
|
||||
|
||||
node.on('synced', function() {
|
||||
// Stop logging of sync status
|
||||
clearInterval(interval);
|
||||
interval = false;
|
||||
logSyncStatus();
|
||||
});
|
||||
|
||||
node.on('ready', function() {
|
||||
|
||||
var io = socketio(configuration.port);
|
||||
|
||||
io.on('connection', function(socket) {
|
||||
|
||||
var bus = node.openBus();
|
||||
|
||||
var methods = node.getAllAPIMethods();
|
||||
var methodsMap = {};
|
||||
|
||||
methods.forEach(function(data) {
|
||||
var name = data[0];
|
||||
var instance = data[1];
|
||||
var method = data[2];
|
||||
var args = data[3];
|
||||
methodsMap[name] = {
|
||||
fn: function() {
|
||||
return method.apply(instance, arguments);
|
||||
},
|
||||
args: args
|
||||
};
|
||||
});
|
||||
|
||||
socket.on('message', function(message, socketCallback) {
|
||||
if (methodsMap[message.method]) {
|
||||
var params = message.params;
|
||||
|
||||
if(!params || !params.length) {
|
||||
params = [];
|
||||
}
|
||||
|
||||
if(params.length !== methodsMap[message.method].args) {
|
||||
return socketCallback({
|
||||
error: {
|
||||
message: 'Expected ' + methodsMap[message.method].args + ' parameters'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var callback = function(err, result) {
|
||||
var response = {};
|
||||
if(err) {
|
||||
response.error = {
|
||||
message: err.toString()
|
||||
};
|
||||
}
|
||||
|
||||
if(result) {
|
||||
response.result = result;
|
||||
}
|
||||
|
||||
socketCallback(response);
|
||||
};
|
||||
|
||||
params = params.concat(callback);
|
||||
methodsMap[message.method].fn.apply(this, params);
|
||||
} else {
|
||||
socketCallback({
|
||||
error: {
|
||||
message: 'Method Not Found'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('subscribe', function(name, params) {
|
||||
bus.subscribe(name, params);
|
||||
});
|
||||
|
||||
socket.on('unsubscribe', function(name, params) {
|
||||
bus.unsubscribe(name, params);
|
||||
});
|
||||
|
||||
var events = node.getAllPublishEvents();
|
||||
|
||||
events.forEach(function(event) {
|
||||
bus.on(event.name, function() {
|
||||
if(socket.connected) {
|
||||
var results = [];
|
||||
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
results.push(arguments[i]);
|
||||
}
|
||||
|
||||
var params = [event.name].concat(results);
|
||||
socket.emit.apply(socket, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
bus.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
node.on('error', function(err) {
|
||||
log.error(err);
|
||||
});
|
||||
|
||||
node.chain.on('addblock', function(block) {
|
||||
count++;
|
||||
// Initialize logging if not already instantiated
|
||||
if (!interval) {
|
||||
interval = setInterval(function() {
|
||||
logSyncStatus();
|
||||
count = 0;
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
node.on('stopping', function() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
|
||||
function exitHandler(options, err) {
|
||||
if (err) {
|
||||
log.error('uncaught exception:', err);
|
||||
if(err.stack) {
|
||||
console.log(err.stack);
|
||||
}
|
||||
process.exit(-1);
|
||||
}
|
||||
if (options.sigint) {
|
||||
node.stop(function(err) {
|
||||
if(err) {
|
||||
log.error('Failed to stop services: ' + err);
|
||||
return process.exit(1);
|
||||
}
|
||||
|
||||
log.info('Halted');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//catches uncaught exceptions
|
||||
|
||||
|
||||
process.on('uncaughtException', exitHandler.bind(null, {exit:true}));
|
||||
//catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {sigint:true}));
|
||||
start(defaultConfig());
|
||||
|
||||
85
cli/bitcore-node.js
Executable file
85
cli/bitcore-node.js
Executable file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
var program = require('commander');
|
||||
var version = require(__dirname + '/../package.json').version;
|
||||
var bitcore = require('bitcore');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var path = require('path');
|
||||
var create = require('../lib/scaffold/create');
|
||||
var add = require('../lib/scaffold/add');
|
||||
var start = require('../lib/scaffold/start');
|
||||
var findConfig = require('../lib/scaffold/find-config');
|
||||
var defaultConfig = require('../lib/scaffold/default-config');
|
||||
|
||||
program
|
||||
.version(version);
|
||||
|
||||
program
|
||||
.command('create <directory> [name]')
|
||||
.description('Create a new node')
|
||||
.option('-d, --datadir <dir>', 'Specify the bitcoin database directory')
|
||||
.action(function(dirname, name, cmd){
|
||||
if (cmd.datadir) {
|
||||
cmd.datadir = path.resolve(process.cwd(), cmd.datadir);
|
||||
}
|
||||
var opts = {
|
||||
cwd: process.cwd(),
|
||||
dirname: dirname,
|
||||
name: name,
|
||||
datadir: cmd.datadir || './data',
|
||||
isGlobal: false
|
||||
};
|
||||
create(opts, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log('Successfully created node in directory: ', dirname);
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
.command('start')
|
||||
.description('Start the current node')
|
||||
.option('-c, --config <dir>', 'Specify the directory with Bitcore Node configuration')
|
||||
.action(function(cmd){
|
||||
if (cmd.config) {
|
||||
cmd.config = path.resolve(process.cwd(), cmd.config);
|
||||
}
|
||||
var configInfo = findConfig(cmd.config || process.cwd());
|
||||
if (!configInfo) {
|
||||
configInfo = defaultConfig();
|
||||
}
|
||||
start(configInfo);
|
||||
});
|
||||
|
||||
program
|
||||
.command('add <modules...>')
|
||||
.alias('install')
|
||||
.description('Install a module for the current node')
|
||||
.action(function(modules){
|
||||
var configInfo = findConfig(process.cwd());
|
||||
if (!configInfo) {
|
||||
throw new Error('Could not find configuration, see `bitcore-node create --help`');
|
||||
}
|
||||
var opts = {
|
||||
path: configInfo.path,
|
||||
modules: modules
|
||||
};
|
||||
add(opts, function() {
|
||||
console.log('Successfully added modules: ', modules.join(', '));
|
||||
});
|
||||
}).on('--help', function() {
|
||||
console.log(' Examples:');
|
||||
console.log();
|
||||
console.log(' $ bitcore-node add wallet-service');
|
||||
console.log(' $ bitcore-node add insight-api');
|
||||
console.log();
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
if (process.argv.length === 2) {
|
||||
program.help();
|
||||
}
|
||||
14
lib/node.js
14
lib/node.js
@ -57,12 +57,11 @@ 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 datadir = config.datadir.replace(/^~/, process.env.HOME);
|
||||
var configPath = datadir + '/bitcoin.conf';
|
||||
var configPath = config.datadir + '/bitcoin.conf';
|
||||
this.bitcoinConfiguration = {};
|
||||
|
||||
if (!fs.existsSync(datadir)) {
|
||||
mkdirp.sync(datadir);
|
||||
if (!fs.existsSync(config.datadir)) {
|
||||
mkdirp.sync(config.datadir);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
@ -329,13 +328,12 @@ Node.prototype._loadDB = function(config) {
|
||||
$.checkArgument(config.datadir, 'Please specify "datadir" in configuration options');
|
||||
$.checkState(this.network, 'Network property not defined');
|
||||
var regtest = Networks.get('regtest');
|
||||
var datadir = config.datadir.replace(/^~/, process.env.HOME);
|
||||
if (this.network === Networks.livenet) {
|
||||
options.path = datadir + '/bitcore-node.db';
|
||||
options.path = config.datadir + '/bitcore-node.db';
|
||||
} else if (this.network === Networks.testnet) {
|
||||
options.path = datadir + '/testnet3/bitcore-node.db';
|
||||
options.path = config.datadir + '/testnet3/bitcore-node.db';
|
||||
} else if (this.network === regtest) {
|
||||
options.path = datadir + '/regtest/bitcore-node.db';
|
||||
options.path = config.datadir + '/regtest/bitcore-node.db';
|
||||
} else {
|
||||
throw new Error('Unknown network: ' + this.network);
|
||||
}
|
||||
|
||||
104
lib/scaffold/add.js
Normal file
104
lib/scaffold/add.js
Normal file
@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var spawn = require('child_process').spawn;
|
||||
var bitcore = require('bitcore');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var _ = bitcore.deps._;
|
||||
|
||||
/**
|
||||
* @param {String} configFilePath - The absolute path to the configuration file
|
||||
* @param {String} module - The name of the module
|
||||
* @param {Function} done
|
||||
*/
|
||||
function addConfig(configFilePath, module, done) {
|
||||
$.checkState(path.isAbsolute(configFilePath), 'An absolute path is expected');
|
||||
fs.readFile(configFilePath, function(err, data) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var config = JSON.parse(data);
|
||||
$.checkState(
|
||||
Array.isArray(config.modules),
|
||||
'Configuration file is expected to have a modules array.'
|
||||
);
|
||||
config.modules.push(module);
|
||||
config.modules = _.unique(config.modules);
|
||||
config.modules.sort(function(a, b) {
|
||||
return a > b;
|
||||
});
|
||||
fs.writeFile(configFilePath, JSON.stringify(config, null, 2), done);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} configDir - The absolute configuration directory path
|
||||
* @param {String} module - The name of the module
|
||||
* @param {Function} done
|
||||
*/
|
||||
function addModule(configDir, module, done) {
|
||||
$.checkState(path.isAbsolute(configDir), 'An absolute path is expected');
|
||||
var npm = spawn('npm', ['install', module, '--save'], {cwd: configDir});
|
||||
|
||||
npm.stdout.on('data', function(data) {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
|
||||
npm.stderr.on('data', function(data) {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
|
||||
npm.on('close', function(code) {
|
||||
if (code !== 0) {
|
||||
return done(new Error('There was an error installing module: ' + module));
|
||||
} else {
|
||||
return done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} options.cwd - The current working directory
|
||||
* @param {String} options.dirname - The bitcore-node configuration directory
|
||||
* @param {Array} options.modules - An array of strings of module names
|
||||
* @param {Function} done - A callback function called when finished
|
||||
*/
|
||||
function add(options, done) {
|
||||
$.checkArgument(_.isObject(options));
|
||||
$.checkArgument(_.isFunction(done));
|
||||
$.checkArgument(
|
||||
_.isString(options.path) && path.isAbsolute(options.path),
|
||||
'An absolute path is expected'
|
||||
);
|
||||
$.checkArgument(Array.isArray(options.modules));
|
||||
|
||||
var configPath = options.path;
|
||||
var modules = options.modules;
|
||||
|
||||
var bitcoreConfigPath = path.resolve(configPath, 'bitcore-node.json');
|
||||
var packagePath = path.resolve(configPath, 'package.json');
|
||||
|
||||
if (!fs.existsSync(bitcoreConfigPath) || !fs.existsSync(packagePath)) {
|
||||
return done(
|
||||
new Error('Directory does not have a bitcore-node.json and/or package.json file.')
|
||||
);
|
||||
}
|
||||
|
||||
async.eachSeries(
|
||||
modules,
|
||||
function(module, next) {
|
||||
// npm install <module_name> --save
|
||||
addModule(configPath, module, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
// add module to bitcore-node.json
|
||||
addConfig(bitcoreConfigPath, module, next);
|
||||
});
|
||||
}, done
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = add;
|
||||
168
lib/scaffold/create.js
Normal file
168
lib/scaffold/create.js
Normal file
@ -0,0 +1,168 @@
|
||||
'use strict';
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var bitcore = require('bitcore');
|
||||
var async = require('async');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var _ = bitcore.deps._;
|
||||
var path = require('path');
|
||||
var packageFile = require('../../package.json');
|
||||
var mkdirp = require('mkdirp');
|
||||
var fs = require('fs');
|
||||
|
||||
var BASE_CONFIG = {
|
||||
name: 'My Node',
|
||||
modules: [
|
||||
'address'
|
||||
],
|
||||
datadir: './data',
|
||||
network: 'livenet',
|
||||
port: 3001
|
||||
};
|
||||
|
||||
var version;
|
||||
if (packageFile.version.match('-dev')) {
|
||||
version = '^' + packageFile.lastBuild;
|
||||
} else {
|
||||
version = '^' + packageFile.version;
|
||||
}
|
||||
|
||||
var BASE_PACKAGE = {
|
||||
dependencies: {
|
||||
'bitcore': '^' + bitcore.version,
|
||||
'bitcore-node': version
|
||||
}
|
||||
};
|
||||
|
||||
var BASE_BITCOIN_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n';
|
||||
|
||||
/**
|
||||
* Will create a directory and bitcoin.conf file for Bitcoin.
|
||||
* @param {String} dataDir - The absolute path
|
||||
* @param {Function} done - The callback function called when finished
|
||||
*/
|
||||
function createBitcoinDirectory(datadir, done) {
|
||||
mkdirp(datadir, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
fs.writeFileSync(datadir + '/bitcoin.conf', BASE_BITCOIN_CONFIG);
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a base Bitcore Node configuration directory and files.
|
||||
* @param {String} configDir - The absolute path
|
||||
* @param {String} name - The name of the node
|
||||
* @param {String} datadir - The bitcoin database directory
|
||||
* @param {Boolean} isGlobal - If the configuration depends on globally installed node modules.
|
||||
* @param {Function} done - The callback function called when finished
|
||||
*/
|
||||
function createConfigDirectory(configDir, name, datadir, isGlobal, done) {
|
||||
mkdirp(configDir, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
var config = BASE_CONFIG;
|
||||
config.name = name || 'Bitcore Node';
|
||||
config.datadir = datadir;
|
||||
var configJSON = JSON.stringify(config, null, 2);
|
||||
var packageJSON = JSON.stringify(BASE_PACKAGE, null, 2);
|
||||
try {
|
||||
fs.writeFileSync(configDir + '/bitcore-node.json', configJSON);
|
||||
if (!isGlobal) {
|
||||
fs.writeFileSync(configDir + '/package.json', packageJSON);
|
||||
}
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Will setup a directory with a Bitcore Node directory, configuration file,
|
||||
* bitcoin configuration, and will install all necessary dependencies.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} options.cwd - The current working directory
|
||||
* @param {String} options.dirname - The name of the bitcore node configuration directory
|
||||
* @param {String} options.name - The name of the bitcore node
|
||||
* @param {String} options.datadir - The path to the bitcoin datadir
|
||||
* @param {Function} done - A callback function called when finished
|
||||
*/
|
||||
function create(options, done) {
|
||||
/* jshint maxstatements:20 */
|
||||
|
||||
$.checkArgument(_.isObject(options));
|
||||
$.checkArgument(_.isFunction(done));
|
||||
$.checkArgument(_.isString(options.cwd));
|
||||
$.checkArgument(_.isString(options.dirname));
|
||||
$.checkArgument(_.isString(options.name) || _.isUndefined(options.name));
|
||||
$.checkArgument(_.isBoolean(options.isGlobal));
|
||||
$.checkArgument(_.isString(options.datadir));
|
||||
|
||||
var cwd = options.cwd;
|
||||
var dirname = options.dirname;
|
||||
var name = options.name;
|
||||
var datadir = options.datadir;
|
||||
var isGlobal = options.isGlobal;
|
||||
|
||||
var absConfigDir = path.resolve(cwd, dirname);
|
||||
var absDataDir = path.resolve(absConfigDir, datadir);
|
||||
|
||||
async.series([
|
||||
function(next) {
|
||||
// Setup the the bitcore-node directory and configuration
|
||||
if (!fs.existsSync(absConfigDir)) {
|
||||
createConfigDirectory(absConfigDir, name, datadir, isGlobal, next);
|
||||
} else {
|
||||
next(new Error('Directory "' + absConfigDir+ '" already exists.'));
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
// Setup the bitcoin directory and configuration
|
||||
if (!fs.existsSync(absDataDir)) {
|
||||
createBitcoinDirectory(absDataDir, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
// Install all of the necessary dependencies
|
||||
if (!isGlobal) {
|
||||
var npm = spawn('npm', ['install'], {cwd: absConfigDir});
|
||||
|
||||
npm.stdout.on('data', function (data) {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
|
||||
npm.stderr.on('data', function (data) {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
|
||||
npm.on('close', function (code) {
|
||||
if (code !== 0) {
|
||||
return next(new Error('There was an error installing dependencies.'));
|
||||
} else {
|
||||
return next();
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
], done);
|
||||
|
||||
}
|
||||
|
||||
module.exports = create;
|
||||
20
lib/scaffold/default-config.js
Normal file
20
lib/scaffold/default-config.js
Normal file
@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
|
||||
/**
|
||||
* Will return the path and default bitcore-node configuration on environment variables
|
||||
* or default locations.
|
||||
*/
|
||||
function getDefaultConfig() {
|
||||
return {
|
||||
path: process.cwd(),
|
||||
config: {
|
||||
datadir: process.env.BITCORENODE_DIR || path.resolve(process.env.HOME, '.bitcoin'),
|
||||
network: process.env.BITCORENODE_NETWORK || 'livenet',
|
||||
port: process.env.BITCORENODE_PORT || 3001
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getDefaultConfig;
|
||||
29
lib/scaffold/find-config.js
Normal file
29
lib/scaffold/find-config.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var bitcore = require('bitcore');
|
||||
var $ = bitcore.util.preconditions;
|
||||
var _ = bitcore.deps._;
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
/**
|
||||
* Will return the path and bitcore-node configuration
|
||||
* @param {String} cwd - The absolute path to the current working directory
|
||||
*/
|
||||
function findConfig(cwd) {
|
||||
$.checkArgument(_.isString(cwd), 'Argument should be a string');
|
||||
$.checkArgument(path.isAbsolute(cwd), 'Argument should be an absolute path');
|
||||
var directory = String(cwd);
|
||||
while (!fs.existsSync(path.resolve(directory, 'bitcore-node.json'))) {
|
||||
directory = path.resolve(directory, '../');
|
||||
if (directory === '/') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return {
|
||||
path: directory,
|
||||
config: require(path.resolve(directory, 'bitcore-node.json'))
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = findConfig;
|
||||
218
lib/scaffold/start.js
Normal file
218
lib/scaffold/start.js
Normal file
@ -0,0 +1,218 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var socketio = require('socket.io');
|
||||
var BitcoreNode = require('../node');
|
||||
var chainlib = require('chainlib');
|
||||
var bitcore = require('bitcore');
|
||||
var _ = bitcore.deps._;
|
||||
var log = chainlib.log;
|
||||
log.debug = function() {};
|
||||
|
||||
var count = 0;
|
||||
var interval = false;
|
||||
|
||||
function start(options) {
|
||||
/* jshint maxstatements: 100 */
|
||||
|
||||
var modules = [];
|
||||
|
||||
var configPath = options.path;
|
||||
var config = options.config;
|
||||
|
||||
if (config.modules) {
|
||||
for (var i = 0; i < config.modules.length; i++) {
|
||||
var moduleName = config.modules[i];
|
||||
var module;
|
||||
try {
|
||||
// first try in the built-in bitcore-node modules directory
|
||||
module = require(path.resolve(__dirname, '../modules/' + moduleName));
|
||||
} catch(e) {
|
||||
// then try loading external modules
|
||||
module = require(moduleName);
|
||||
}
|
||||
|
||||
modules.push(module);
|
||||
}
|
||||
}
|
||||
|
||||
var fullConfig = _.clone(config);
|
||||
|
||||
// expand to the full path
|
||||
fullConfig.datadir = path.resolve(configPath, config.datadir);
|
||||
|
||||
// delete until modules move to the node
|
||||
delete fullConfig.modules;
|
||||
|
||||
// load the modules
|
||||
fullConfig.db = {
|
||||
modules: modules
|
||||
};
|
||||
|
||||
var node = new BitcoreNode(fullConfig);
|
||||
|
||||
function logSyncStatus() {
|
||||
log.info(
|
||||
'Sync Status: Tip:', node.chain.tip.hash,
|
||||
'Height:', node.chain.tip.__height,
|
||||
'Rate:', count/10, 'blocks per second'
|
||||
);
|
||||
}
|
||||
|
||||
node.on('synced', function() {
|
||||
// Stop logging of sync status
|
||||
clearInterval(interval);
|
||||
interval = false;
|
||||
logSyncStatus();
|
||||
});
|
||||
|
||||
node.on('ready', function() {
|
||||
|
||||
var io = socketio(fullConfig.port);
|
||||
|
||||
io.on('connection', function(socket) {
|
||||
|
||||
var bus = node.openBus();
|
||||
|
||||
var methods = node.getAllAPIMethods();
|
||||
var methodsMap = {};
|
||||
|
||||
methods.forEach(function(data) {
|
||||
var name = data[0];
|
||||
var instance = data[1];
|
||||
var method = data[2];
|
||||
var args = data[3];
|
||||
methodsMap[name] = {
|
||||
fn: function() {
|
||||
return method.apply(instance, arguments);
|
||||
},
|
||||
args: args
|
||||
};
|
||||
});
|
||||
|
||||
socket.on('message', function(message, socketCallback) {
|
||||
if (methodsMap[message.method]) {
|
||||
var params = message.params;
|
||||
|
||||
if(!params || !params.length) {
|
||||
params = [];
|
||||
}
|
||||
|
||||
if(params.length !== methodsMap[message.method].args) {
|
||||
return socketCallback({
|
||||
error: {
|
||||
message: 'Expected ' + methodsMap[message.method].args + ' parameters'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var callback = function(err, result) {
|
||||
var response = {};
|
||||
if(err) {
|
||||
response.error = {
|
||||
message: err.toString()
|
||||
};
|
||||
}
|
||||
|
||||
if(result) {
|
||||
response.result = result;
|
||||
}
|
||||
|
||||
socketCallback(response);
|
||||
};
|
||||
|
||||
params = params.concat(callback);
|
||||
methodsMap[message.method].fn.apply(this, params);
|
||||
} else {
|
||||
socketCallback({
|
||||
error: {
|
||||
message: 'Method Not Found'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('subscribe', function(name, params) {
|
||||
bus.subscribe(name, params);
|
||||
});
|
||||
|
||||
socket.on('unsubscribe', function(name, params) {
|
||||
bus.unsubscribe(name, params);
|
||||
});
|
||||
|
||||
var events = node.getAllPublishEvents();
|
||||
|
||||
events.forEach(function(event) {
|
||||
bus.on(event.name, function() {
|
||||
if(socket.connected) {
|
||||
var results = [];
|
||||
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
results.push(arguments[i]);
|
||||
}
|
||||
|
||||
var params = [event.name].concat(results);
|
||||
socket.emit.apply(socket, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
bus.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
node.on('error', function(err) {
|
||||
log.error(err);
|
||||
});
|
||||
|
||||
node.chain.on('addblock', function(block) {
|
||||
count++;
|
||||
// Initialize logging if not already instantiated
|
||||
if (!interval) {
|
||||
interval = setInterval(function() {
|
||||
logSyncStatus();
|
||||
count = 0;
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
node.on('stopping', function() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
|
||||
function exitHandler(options, err) {
|
||||
if (err) {
|
||||
log.error('uncaught exception:', err);
|
||||
if(err.stack) {
|
||||
console.log(err.stack);
|
||||
}
|
||||
process.exit(-1);
|
||||
}
|
||||
if (options.sigint) {
|
||||
node.stop(function(err) {
|
||||
if(err) {
|
||||
log.error('Failed to stop services: ' + err);
|
||||
return process.exit(1);
|
||||
}
|
||||
|
||||
log.info('Halted');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler.bind(null, {exit:true}));
|
||||
|
||||
//catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler.bind(null, {sigint:true}));
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
module.exports = start;
|
||||
@ -3,7 +3,9 @@
|
||||
"description": "Full node with extended capabilities using Bitcore and Bitcoin Core",
|
||||
"author": "BitPay <dev@bitpay.com>",
|
||||
"version": "0.2.0-dev",
|
||||
"lastBuild": "0.2.0-beta.3",
|
||||
"main": "./index.js",
|
||||
"bin": "./cli/bitcore-node.js",
|
||||
"repository": "git://github.com/bitpay/bitcore-node.git",
|
||||
"homepage": "https://github.com/bitpay/bitcore-node.js",
|
||||
"bugs": {
|
||||
@ -46,6 +48,7 @@
|
||||
"bindings": "^1.2.1",
|
||||
"bitcore": "^0.13.0",
|
||||
"chainlib": "^0.2.0",
|
||||
"commander": "^2.8.1",
|
||||
"errno": "^0.1.2",
|
||||
"memdown": "^1.0.0",
|
||||
"mkdirp": "0.5.0",
|
||||
|
||||
@ -101,7 +101,7 @@ describe('Bitcoind Node', function() {
|
||||
describe('#_loadBitcoinConf', function() {
|
||||
it('will parse a bitcoin.conf file', function() {
|
||||
var node = new Node({});
|
||||
node._loadBitcoinConf({datadir: '~/.bitcoin'});
|
||||
node._loadBitcoinConf({datadir: process.env.HOME + '/.bitcoin'});
|
||||
should.exist(node.bitcoinConfiguration);
|
||||
node.bitcoinConfiguration.should.deep.equal({
|
||||
server: 1,
|
||||
@ -349,7 +349,7 @@ describe('Bitcoind Node', function() {
|
||||
};
|
||||
var config = {
|
||||
DB: DB,
|
||||
datadir: '~/.bitcoin'
|
||||
datadir: process.env.HOME + '/.bitcoin'
|
||||
};
|
||||
|
||||
var node = new Node(config);
|
||||
@ -363,7 +363,7 @@ describe('Bitcoind Node', function() {
|
||||
};
|
||||
var config = {
|
||||
DB: DB,
|
||||
datadir: '~/.bitcoin'
|
||||
datadir: process.env.HOME + '/.bitcoin'
|
||||
};
|
||||
|
||||
var node = new Node(config);
|
||||
@ -373,7 +373,7 @@ describe('Bitcoind Node', function() {
|
||||
});
|
||||
it('error with unknown network', function() {
|
||||
var config = {
|
||||
datadir: '~/.bitcoin'
|
||||
datadir: process.env.HOME + '/.bitcoin'
|
||||
};
|
||||
|
||||
var node = new Node(config);
|
||||
@ -388,7 +388,7 @@ describe('Bitcoind Node', function() {
|
||||
};
|
||||
var config = {
|
||||
DB: DB,
|
||||
datadir: '~/.bitcoin'
|
||||
datadir: process.env.HOME + '/.bitcoin'
|
||||
};
|
||||
|
||||
var node = new Node(config);
|
||||
|
||||
122
test/scaffold/add.integration.js
Normal file
122
test/scaffold/add.integration.js
Normal file
@ -0,0 +1,122 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var proxyquire = require('proxyquire');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('rimraf');
|
||||
var add = require('../../lib/scaffold/add');
|
||||
|
||||
describe('#add', function() {
|
||||
|
||||
var basePath = path.resolve(__dirname, '..');
|
||||
var testDir = path.resolve(basePath, 'temporary-test-data');
|
||||
var startConfig = {
|
||||
name: 'My Node',
|
||||
modules: []
|
||||
};
|
||||
var startPackage = {};
|
||||
|
||||
before(function(done) {
|
||||
mkdirp(testDir + '/s0/s1', function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
fs.writeFile(
|
||||
testDir + '/s0/s1/bitcore-node.json',
|
||||
JSON.stringify(startConfig),
|
||||
function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
fs.writeFile(
|
||||
testDir + '/s0/s1/package.json',
|
||||
JSON.stringify(startPackage),
|
||||
done
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
// cleanup testing directories
|
||||
rimraf(testDir, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('will modify scaffold files', function() {
|
||||
|
||||
it('will give an error if expected files do not exist', function(done) {
|
||||
add({
|
||||
path: path.resolve(testDir, 's0'),
|
||||
modules: ['a', 'b', 'c']
|
||||
}, function(err) {
|
||||
should.exist(err);
|
||||
err.message.match(/^Invalid state/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('will receive error from `npm install`', function(done) {
|
||||
var spawn = sinon.stub().returns({
|
||||
stdout: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
stderr: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
on: sinon.stub().callsArgWith(1, 1)
|
||||
});
|
||||
var addtest = proxyquire('../../lib/scaffold/add', {
|
||||
'child_process': {
|
||||
spawn: spawn
|
||||
}
|
||||
});
|
||||
|
||||
addtest({
|
||||
path: path.resolve(testDir, 's0/s1/'),
|
||||
modules: ['a', 'b', 'c']
|
||||
}, function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('There was an error installing module: a');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('will update bitcore-node.json modules', function(done) {
|
||||
var spawn = sinon.stub().returns({
|
||||
stdout: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
stderr: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
on: sinon.stub().callsArgWith(1, 0)
|
||||
});
|
||||
var addtest = proxyquire('../../lib/scaffold/add', {
|
||||
'child_process': {
|
||||
spawn: spawn
|
||||
}
|
||||
});
|
||||
addtest({
|
||||
path: path.resolve(testDir, 's0/s1/'),
|
||||
modules: ['a', 'b', 'c']
|
||||
}, function(err) {
|
||||
should.not.exist(err);
|
||||
var configPath = path.resolve(testDir, 's0/s1/bitcore-node.json');
|
||||
var config = JSON.parse(fs.readFileSync(configPath));
|
||||
config.modules.should.deep.equal(['a','b','c']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
174
test/scaffold/create.integration.js
Normal file
174
test/scaffold/create.integration.js
Normal file
@ -0,0 +1,174 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('chai').should();
|
||||
var proxyquire = require('proxyquire');
|
||||
var sinon = require('sinon');
|
||||
var create = proxyquire('../../lib/scaffold/create', {
|
||||
'child_process': {
|
||||
spawn: sinon.stub().returns({
|
||||
stdout: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
stderr: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
on: function(event, cb) {
|
||||
cb(0);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
var fs = require('fs');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('rimraf');
|
||||
|
||||
describe('#create', function() {
|
||||
|
||||
var basePath = __dirname + '/../';
|
||||
var testDir = basePath + 'temporary-test-data';
|
||||
|
||||
before(function(done) {
|
||||
// setup testing directories
|
||||
mkdirp(testDir, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
mkdirp(testDir + '/.bitcoin', function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
// cleanup testing directories
|
||||
rimraf(testDir, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('will create scaffold files', function() {
|
||||
|
||||
create({
|
||||
cwd: testDir,
|
||||
dirname: 'mynode',
|
||||
name: 'My Node 1',
|
||||
isGlobal: false,
|
||||
datadir: './data'
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
var configPath = testDir + '/mynode/bitcore-node.json';
|
||||
var packagePath = testDir + '/mynode/package.json';
|
||||
var bitcoinConfig = testDir + '/mynode/data/bitcoin.conf';
|
||||
|
||||
should.equal(fs.existsSync(configPath), true);
|
||||
should.equal(fs.existsSync(packagePath), true);
|
||||
should.equal(fs.existsSync(bitcoinConfig), true);
|
||||
|
||||
var config = JSON.parse(fs.readFileSync(configPath));
|
||||
config.name.should.equal('My Node 1');
|
||||
config.modules.should.deep.equal(['address']);
|
||||
config.datadir.should.equal('./data');
|
||||
config.network.should.equal('livenet');
|
||||
|
||||
var pack = JSON.parse(fs.readFileSync(packagePath));
|
||||
should.exist(pack.dependencies);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('will error if directory already exists', function() {
|
||||
|
||||
create({
|
||||
cwd: testDir,
|
||||
dirname: 'mynode',
|
||||
name: 'My Node 2',
|
||||
isGlobal: false,
|
||||
datadir: './data'
|
||||
}, function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.match(/^Directory/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('will not create bitcoin.conf if it already exists', function() {
|
||||
|
||||
create({
|
||||
cwd: testDir,
|
||||
dirname: 'mynode2',
|
||||
name: 'My Node 2',
|
||||
isGlobal: false,
|
||||
datadir: '../.bitcoin'
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
var bitcoinConfig = testDir + '/.bitcoin/bitcoin.conf';
|
||||
should.equal(fs.existsSync(bitcoinConfig), false);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('will not create a package.json if globally installed', function() {
|
||||
|
||||
create({
|
||||
cwd: testDir,
|
||||
dirname: 'mynode3',
|
||||
name: 'My Node 3',
|
||||
isGlobal: true,
|
||||
datadir: '../.bitcoin'
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
var packagePath = testDir + '/mynode3/package.json';
|
||||
should.equal(fs.existsSync(packagePath), false);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('will receieve an error from npm', function() {
|
||||
var createtest = proxyquire('../../lib/scaffold/create', {
|
||||
'child_process': {
|
||||
spawn: sinon.stub().returns({
|
||||
stdout: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
stderr: {
|
||||
on: sinon.stub()
|
||||
},
|
||||
on: function(event, cb) {
|
||||
cb(1);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
createtest({
|
||||
cwd: testDir,
|
||||
dirname: 'mynode4',
|
||||
name: 'My Node 4',
|
||||
isGlobal: false,
|
||||
datadir: '../.bitcoin'
|
||||
}, function(err) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('There was an error installing dependencies.');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
79
test/scaffold/find-config.integration.js
Normal file
79
test/scaffold/find-config.integration.js
Normal file
@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('rimraf');
|
||||
|
||||
var findConfig = require('../../lib/scaffold/find-config');
|
||||
|
||||
describe('#findConfig', function() {
|
||||
|
||||
var testDir = path.resolve(__dirname, '../temporary-test-data');
|
||||
var expectedConfig = {
|
||||
name: 'My Node'
|
||||
};
|
||||
|
||||
before(function(done) {
|
||||
// setup testing directories
|
||||
mkdirp(testDir + '/p2/p1/p0', function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
fs.writeFile(
|
||||
testDir + '/p2/bitcore-node.json',
|
||||
JSON.stringify(expectedConfig),
|
||||
function() {
|
||||
mkdirp(testDir + '/e0', function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
// cleanup testing directories
|
||||
rimraf(testDir, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('will find a configuration file', function() {
|
||||
|
||||
it('in the current directory', function() {
|
||||
var config = findConfig(path.resolve(testDir, 'p2'));
|
||||
config.path.should.equal(path.resolve(testDir, 'p2'));
|
||||
config.config.should.deep.equal(expectedConfig);
|
||||
});
|
||||
|
||||
it('in a parent directory', function() {
|
||||
var config = findConfig(path.resolve(testDir, 'p2/p1'));
|
||||
config.path.should.equal(path.resolve(testDir, 'p2'));
|
||||
config.config.should.deep.equal(expectedConfig);
|
||||
});
|
||||
|
||||
it('recursively find in parent directories', function() {
|
||||
var config = findConfig(path.resolve(testDir, 'p2/p1/p0'));
|
||||
config.path.should.equal(path.resolve(testDir, 'p2'));
|
||||
config.config.should.deep.equal(expectedConfig);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('will return false if missing a configuration', function() {
|
||||
var config = findConfig(path.resolve(testDir, 'e0'));
|
||||
config.should.equal(false);
|
||||
});
|
||||
|
||||
});
|
||||
39
test/scaffold/start.integration.js
Normal file
39
test/scaffold/start.integration.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var proxyquire = require('proxyquire');
|
||||
var AddressModule = require('../../lib/modules/address');
|
||||
|
||||
describe('#start', function() {
|
||||
|
||||
describe('will dynamically create a node from a configuration', function() {
|
||||
|
||||
it('require each bitcore-node module', function(done) {
|
||||
var node;
|
||||
var TestNode = function(options) {
|
||||
options.db.modules.should.deep.equal([AddressModule]);
|
||||
};
|
||||
TestNode.prototype.on = sinon.stub();
|
||||
TestNode.prototype.chain = {
|
||||
on: sinon.stub()
|
||||
};
|
||||
|
||||
var starttest = proxyquire('../../lib/scaffold/start', {
|
||||
'../node': TestNode
|
||||
});
|
||||
|
||||
node = starttest({
|
||||
path: __dirname,
|
||||
config: {
|
||||
modules: [
|
||||
'address'
|
||||
],
|
||||
datadir: './data'
|
||||
}
|
||||
});
|
||||
node.should.be.instanceof(TestNode);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user