UNPKG

coinlib

Version:

A coin daemon interraction library aiming to support as many crypto-currencies as possible

207 lines (158 loc) 7.28 kB
// // coinlib // Copyright (C) 2013 - 2014, int6ware // http://www.int6ware.com - https://github.com/int6/coinlib // // var events = require('events'); var http = require('http'); var winston = require('winston'); var errors = require('./errors.js'); var client = module.exports = function (config) { var _this = this; // free the limits of http connections. http.globalAgent.maxSockets = Infinity; // store the config _this.config = config; // api functions. this.cmd = cmd; this.batch = batch; // try to connect the configured daemon waitDaemonConnectivity(function () { // wait until block-chain is in sync waitBlockchainSync(function (error) { if(error !== null) _this.emit('online'); }); }); function cmd(method, params, callback) { var request = JSON.stringify({ method: method, params: params, id: Date.now() + Math.floor(Math.random() * 10) }); makeHttpRequest(request, function (error, result) { callback(error, result); }); } function batch(methods, callback) { var requests = []; for (var i = 0; i < methods.length; i++) { requests.push({ method: methods[i][0], params: methods[i][1], id: Date.now() + Math.floor(Math.random() * 10) + i }); } var json = JSON.stringify(requests); makeHttpRequest(json, function (error, results) { callback(error, results); }); } function waitDaemonConnectivity(callback) { winston.log('debug', 'Checking coin daemon connectivity [%s:%d]', config.host, config.port); var retryCount = 0; var check = function () { cmd('getinfo', [], function (error, response) { if (error !== null || response === 'undefined' || response.error) { // case when we can't get a response back from coin daemon. retryCount++; winston.log('debug', 'Waiting for coin daemon [%s:%d] to come online - retry: %d', config.host, config.port, retryCount); setTimeout(check, 1000); } else if (response.result.connections === 0) { // case where coin daemon is not connected to coin network (getinfo.connections = 0) retryCount++; winston.log('debug', 'Waiting for coin daemon [%s:%d] to get connected - retry: %d', config.host, config.port, retryCount); setTimeout(check, 1000); } else { // once we can get a reply back from coin daemon and it's connected to other peers, we are ready to rock winston.log('debug', 'Daemon connectivity check succesfull [%s:%d]', config.host, config.port); callback(); } }); }; check(); } function waitBlockchainSync(callback) { winston.log('debug', 'Waiting for block-chain synchronization'); var check = function () { var calls = [ ['getblocktemplate', []], ['getinfo', []], ['getpeerinfo', []] ]; batch(calls, function (error, responses) { var results = []; responses.forEach(function (response, index) { var call = calls[index][0]; results[call] = response; if (response.error) { winston.log('debug', 'Initilization failed as rpc call ' + call + ' failed: ' + response.error.message); callback(error); } }); var blocktemplate = results.getblocktemplate; var synced = !blocktemplate.result.error || blocktemplate.result.error.code !== errors.Rpc.CLIENT_IN_INITIAL_DOWNLOAD; var blockCount = results.getinfo.result.blocks; var peers = results.getpeerinfo.result; if (synced) { winston.log('debug', 'Block chain is on sync with %d peers [total blocks: %d]', peers.length, blockCount); callback(); } else { setTimeout(check, 500); var sorted = peers.sort(function (a, b) { return b.startingheight - a.startingheight; }); var longestChain = sorted[0].startingheight; var percent = (blockCount / longestChain * 100).toFixed(2); winston.log('debug', 'Waiting for block chain syncronization, downloaded ' + percent + '% from ' + peers.length + ' peers'); } }); }; check(); } function makeHttpRequest(requestData, callback) { var options = { hostname: (typeof (_this.config.host) == 'undefined' ? '127.0.0.1' : _this.config.host), // default to 127.0.0.1 port: _this.config.port, method: 'POST', auth: _this.config.username + ':' + _this.config.password, headers: { 'Content-Length': requestData.length } } var request = http.request(options, function (response) { var data = ''; response.setEncoding('utf8'); response.on('data', function (chunk) { data += chunk; }); response.on('end', function () { parseResponse(response, data); }); }); request.on('error', function (e) { callback(e); }); request.end(requestData); var parseResponse = function (response, responseData) { var json; // handle 401 - Unauthorized if (response.statusCode === 401) { winston.log('debug', 'Unauthorized RPC access; invalid username or password'); callback('Invalid username or password'); } try { json = JSON.parse(responseData); } catch (e) { winston.log('debug', 'Could not parse rpc data from coin daemon; \n request: ' + requestData + '\nresponse: ' + responseData); callback(e); } if (json) callback(json.error, json); } } } client.prototype.__proto__ = events.EventEmitter.prototype;