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

352 lines
9.9 KiB
JavaScript
Raw Normal View History

2015-08-31 06:00:00 -07:00
'use strict';
2015-09-16 10:35:54 -07:00
var fs = require('fs');
2015-08-31 06:00:00 -07:00
var util = require('util');
var bindings = require('bindings')('bitcoind.node');
var mkdirp = require('mkdirp');
2015-10-16 21:56:29 -07:00
var bitcore = require('bitcore-lib');
2015-08-31 06:00:00 -07:00
var $ = bitcore.util.preconditions;
2015-08-31 06:00:00 -07:00
var index = require('../');
var log = index.log;
2015-08-31 06:00:00 -07:00
var Service = require('../service');
2015-08-31 06:00:00 -07:00
/**
* Provides an interface to native bindings to [Bitcoin Core](https://github.com/bitcoin/bitcoin)
* compiled as a static library. The C++ bindings can be found at `src/libbitcoind.cc`
2015-08-31 06:00:00 -07:00
* @param {Object} options
* @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;
2015-08-31 06:00:00 -07:00
Service.call(this, options);
2015-08-31 06:00:00 -07:00
$.checkState(this.node.datadir, 'Node is missing datadir property');
}
2015-08-31 06:00:00 -07:00
util.inherits(Bitcoin, Service);
2015-08-31 06:00:00 -07:00
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)) {
2015-09-10 08:06:37 -07:00
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);
2015-08-31 06:00:00 -07:00
}
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. ' +
2015-08-31 06:00:00 -07:00
'Please add "txindex=1" to your configuration and reindex an existing database if ' +
2015-08-31 06:00:00 -07:00
'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;
}
2015-08-31 06:00:00 -07:00
};
2015-08-31 08:09:24 -07:00
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 Height:', this.height, 'Percentage:', percentage);
2015-08-31 08:09:24 -07:00
}
// 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();
2015-08-31 08:09:24 -07:00
});
};
2015-09-22 08:38:14 -07:00
/**
* Called by Node to start the service
* @param {Function} callback
*/
2015-08-31 06:00:00 -07:00
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);
}
2015-08-31 08:09:24 -07:00
// Wait until the block chain is ready
2015-08-31 06:00:00 -07:00
bindings.onBlocksReady(function(err, result) {
2015-08-31 08:09:24 -07:00
if (err) {
return callback(err);
2015-08-31 06:00:00 -07:00
}
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);
}
2015-08-31 06:00:00 -07:00
});
});
};
2015-09-22 08:38:14 -07:00
/**
* Helper to determine the state of the database.
* @returns {Boolean} If the database is fully synced
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.isSynced = function() {
return bindings.isSynced();
};
2015-09-22 08:38:14 -07:00
/**
* Helper to determine the progress of the database.
* @returns {Number} An estimated percentage of the syncronization status
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.syncPercentage = function() {
return bindings.syncPercentage();
};
2015-09-22 08:38:14 -07:00
/**
* Will retrieve a block as a Node.js Buffer from disk.
2015-09-22 08:38:14 -07:00
* @param {String|Number} block - A block hash or block height number
*/
Bitcoin.prototype.getBlock = function(block, callback) {
return bindings.getBlock(block, callback);
2015-08-31 06:00:00 -07:00
};
2015-09-22 08:38:14 -07:00
/**
* Will return the spent status of an output (not including the mempool)
* @param {String} txid - The transaction hash
* @param {Number} outputIndex - The output index in the transaction
* @returns {Boolean} If the output has been spent
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.isSpent = function(txid, outputIndex) {
return bindings.isSpent(txid, outputIndex);
};
2015-09-22 08:38:14 -07:00
/**
* Will return the block index information, the output will have the format:
* {
* prevHash: '7194fcf33f58c96720f88f21ab28c34ebc5638c5f88d7838517deb27313b59de',
* hash: '7c5caf0af1bf16e3467b275a3b408bc1d251bff3c25be20cb727c47b66a7b216',
* chainWork: '0000000000000000000000000000000000000000000000000000000000000016',
* height: 10
* }
* @param {String|Number} block - A block hash or block height
* @returns {Object}
*/
Bitcoin.prototype.getBlockIndex = function(block) {
return bindings.getBlockIndex(block);
2015-08-31 06:00:00 -07:00
};
2015-09-22 08:38:14 -07:00
/**
* Will return if the block is a part of the main chain.
* @param {String} blockHash
* @returns {Boolean}
*/
2015-09-08 11:04:14 -07:00
Bitcoin.prototype.isMainChain = function(blockHash) {
return bindings.isMainChain(blockHash);
};
2015-09-22 08:38:14 -07:00
/**
* Will estimate the fee per kilobyte.
* @param {Number} blocks - The number of blocks for the transaction to be confirmed.
* @returns {Number}
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.estimateFee = function(blocks) {
return bindings.estimateFee(blocks);
};
2015-09-22 08:38:14 -07:00
/**
* Will add a transaction to the mempool and relay to connected peers, the function
* will throw an error if there were validation problems.
* @param {String} transaction - The hex string of the transaction
* @param {Boolean} allowAbsurdFees - Enable large fees
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.sendTransaction = function(transaction, allowAbsurdFees) {
return bindings.sendTransaction(transaction, allowAbsurdFees);
};
2015-09-22 08:38:14 -07:00
/**
* Will get a transaction as a Node.js Buffer from disk and the mempool.
* @param {String} txid - The transaction hash
* @param {Boolean} queryMempool - Include the mempool
* @param {Function} callback
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.getTransaction = function(txid, queryMempool, callback) {
return bindings.getTransaction(txid, queryMempool, callback);
};
2015-09-22 08:38:14 -07:00
/**
* Will get a transaction with additional information about the block, in the format:
2015-09-22 08:38:14 -07:00
* {
* blockHash: '2725743288feae6bdaa976590af7cb12d7b535b5a242787de6d2789c73682ed1',
* height: 48,
* timestamp: 1442951110, // in seconds
* buffer: <Buffer...> // transaction buffer
* }
* @param {String} txid - The transaction hash
* @param {Boolean} queryMempool - Include the mempool
* @param {Function} callback
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) {
return bindings.getTransactionWithBlockInfo(txid, queryMempool, callback);
};
2015-09-22 08:38:14 -07:00
/**
* Will return the entire mempool as an Array of transaction Buffers.
* @returns {Array}
*/
2015-09-16 10:35:54 -07:00
Bitcoin.prototype.getMempoolTransactions = function() {
return bindings.getMempoolTransactions();
2015-08-31 06:00:00 -07:00
};
2015-09-22 08:38:14 -07:00
/**
* Will add a transaction to the mempool without any validation. This is used
* exclusively for testing purposes.
* @param {String} transaction - The hex string for the transaction
*/
Bitcoin.prototype.addMempoolUncheckedTransaction = function(transaction) {
return bindings.addMempoolUncheckedTransaction(transaction);
2015-08-31 06:00:00 -07:00
};
2015-09-22 08:38:14 -07:00
/**
* Will get the best block hash for the chain.
* @returns {String}
*/
Bitcoin.prototype.getBestBlockHash = function() {
return bindings.getBestBlockHash();
};
2015-09-22 08:38:14 -07:00
/**
* Will get the next block hash for a block hash.
* @param {String} hash - The starting block hash
* @returns {String}
*/
Bitcoin.prototype.getNextBlockHash = function(hash) {
return bindings.getNextBlockHash(hash);
};
2015-09-22 08:38:14 -07:00
/**
* This will return information about the database in the format:
* {
* version: 110000,
* protocolversion: 70002,
* blocks: 151,
* timeoffset: 0,
* connections: 0,
* difficulty: 4.6565423739069247e-10,
* testnet: false,
* relayfee: 1000,
* errors: ''
* }
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.getInfo = function() {
return bindings.getInfo();
};
2015-09-22 08:38:14 -07:00
/**
* Called by Node to stop the service.
* @param {Function} callback
*/
2015-08-31 06:00:00 -07:00
Bitcoin.prototype.stop = function(callback) {
return bindings.stop(function(err, status) {
if (err) {
return callback(err);
} else {
log.info(status);
return callback();
}
2015-08-31 06:00:00 -07:00
});
};
module.exports = Bitcoin;