bitcore-node-zcash/lib/services/bitcoind.js

255 lines
6.9 KiB
JavaScript

'use strict';
var fs = require('fs');
var util = require('util');
var bindings = require('bindings')('bitcoind.node');
var mkdirp = require('mkdirp');
var async = require('async');
var bitcore = require('bitcore');
var Transaction = require('../transaction');
var $ = bitcore.util.preconditions;
var index = require('../');
var log = index.log;
var Service = require('../service');
/**
* 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);
}
this._reindex = false;
this._reindexWait = 1000;
Service.call(this, options);
$.checkState(this.node.datadir, 'Node is missing datadir property');
}
util.inherits(Bitcoin, Service);
Bitcoin.dependencies = [];
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)) {
var defaultConfig = Bitcoin.DEFAULT_CONFIG;
if(this.node.https && this.node.httpsOptions) {
defaultConfig += 'rpcssl=1\n';
defaultConfig += 'rpcsslprivatekeyfile=' + this.node.httpsOptions.key + '\n';
defaultConfig += 'rpcsslcertificatechainfile=' + this.node.httpsOptions.cert + '\n';
}
fs.writeFileSync(configPath, defaultConfig);
}
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'
);
if (this.configuration.reindex && this.configuration.reindex === 1) {
log.warn('Reindex option is currently enabled. This means that bitcoind is undergoing a reindex. ' +
'The reindex flag will start the index from beginning every time the node is started, so it ' +
'should be removed after the reindex has been initiated. Once the reindex is complete, the rest ' +
'of bitcore-node services will start.');
this._reindex = true;
}
};
Bitcoin.prototype._onTipUpdate = function(result) {
if (result) {
// Emit and event that the tip was updated
this.height = result;
this.emit('tip', result);
// TODO stopping status
if(!this.node.stopping) {
var percentage = this.syncPercentage();
log.info('Bitcoin Core Daemon New Height:', this.height, 'Percentage:', percentage);
}
// Recursively wait until the next update
bindings.onTipUpdate(this._onTipUpdate.bind(this));
}
};
Bitcoin.prototype._registerEventHandlers = function() {
var self = this;
// Set the height and emit a new tip
bindings.onTipUpdate(self._onTipUpdate.bind(this));
// Register callback function to handle incoming transactions
bindings.startTxMon(function(txs) {
for(var i = 0; i < txs.length; i++) {
self.emit('tx', txs[i]);
}
});
};
Bitcoin.prototype._onReady = function(result, callback) {
var self = this;
self._registerEventHandlers();
var info = self.getInfo();
self.height = info.blocks;
self.getBlock(0, function(err, block) {
if (err) {
return callback(err);
}
self.genesisBuffer = block;
self.emit('ready', result);
log.info('Bitcoin Daemon Ready');
callback();
});
};
Bitcoin.prototype.start = function(callback) {
var self = this;
this._loadConfiguration();
bindings.start({
datadir: this.node.datadir,
network: this.node.network.name
}, function(err) {
if(err) {
return callback(err);
}
// Wait until the block chain is ready
bindings.onBlocksReady(function(err, result) {
if (err) {
return callback(err);
}
if (self._reindex) {
var interval = setInterval(function() {
var percentSynced = bindings.syncPercentage();
log.info("Bitcoin Core Daemon Reindex Percentage: " + percentSynced);
if (percentSynced >= 100) {
self._reindex = false;
self._onReady(result, callback);
clearInterval(interval);
}
}, self._reindexWait);
}
else {
self._onReady(result, 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.isMainChain = function(blockHash) {
return bindings.isMainChain(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.getMempoolTransactions = function() {
return bindings.getMempoolTransactions();
};
Bitcoin.prototype.addMempoolUncheckedTransaction = function(txBuffer) {
return bindings.addMempoolUncheckedTransaction(txBuffer);
};
Bitcoin.prototype.getBestBlockHash = function() {
return bindings.getBestBlockHash();
};
Bitcoin.prototype.getNextBlockHash = function(hash) {
return bindings.getNextBlockHash(hash);
};
Bitcoin.prototype.getTxOutSetInfo = function() {
return bindings.getTxOutSetInfo();
};
Bitcoin.prototype.getInfo = function() {
return bindings.getInfo();
};
Bitcoin.prototype.stop = function(callback) {
return bindings.stop(function(err, status) {
if (err) {
return callback(err);
} else {
log.info(status);
return callback();
}
});
};
module.exports = Bitcoin;