Merge pull request #137 from matiu/feature/02livenet

Feature/02livenet
This commit is contained in:
Mario Colque 2014-01-23 10:22:58 -08:00
commit 2d1f3191e3
10 changed files with 113 additions and 110 deletions

View File

@ -167,7 +167,20 @@ There is a bitcoind configuration sample at:
etc/bitcoind/bitcoin.conf etc/bitcoind/bitcoin.conf
``` ```
If you want to use a external bitcoind server set BITCOIND_HOST / BITCOIND_PORT enviroment variables. Make sure that bitcoind is configured to accept incomming connections using 'rpcallowip' decribed in https://en.bitcoin.it/wiki/Running_Bitcoin. If you want to use a external bitcoind server set:
BITCOIND_HOST
BITCOIND_PORT # RPC Bitcoind Port
BITCOIND_P2P_PORT # P2P Bitcoind Port
BITCOIND_USER
BITCOIND_PASS
INSIGHT_NETWORK [= 'livenet' | 'testnet']
enviroment variables. Make sure that bitcoind is configured to accept incomming connections using 'rpcallowip' decribed in https://en.bitcoin.it/wiki/Running_Bitcoin. Alternatively change config/env/$NODE_ENV.js
In case the network is changed, mongoDB database need to be deleted. This can be performed running:
```
util/sync.js -D
```
### Environment Variables Settings ### Environment Variables Settings
@ -192,6 +205,8 @@ To run insight locally for development:
$ NODE_ENV=development grunt $ NODE_ENV=development grunt
### Production ### Production
You can use [pm2](https://github.com/Unitech/pm2) to manage NodeJS in production: You can use [pm2](https://github.com/Unitech/pm2) to manage NodeJS in production:

View File

@ -15,17 +15,17 @@ module.exports.init = function(app, io_ext) {
}; };
module.exports.broadcast_tx = function(tx) { module.exports.broadcast_tx = function(tx) {
ios.sockets.in('inv').emit('tx', tx); if (ios) ios.sockets.in('inv').emit('tx', tx);
}; };
module.exports.broadcast_block = function(block) { module.exports.broadcast_block = function(block) {
ios.sockets.in('inv').emit('block', block); if (ios) ios.sockets.in('inv').emit('block', block);
}; };
module.exports.broadcast_address_tx = function(address, tx) { module.exports.broadcast_address_tx = function(address, tx) {
ios.sockets.in(address).emit(address, tx); if (ios) ios.sockets.in(address).emit(address, tx);
}; };
module.exports.broadcastSyncInfo = function(historicSync) { module.exports.broadcastSyncInfo = function(historicSync) {
ios.sockets.in('sync').emit('status', historicSync); if (ios) ios.sockets.in('sync').emit('status', historicSync);
}; };

View File

@ -11,6 +11,7 @@ module.exports = {
pass: process.env.BITCOIND_PASS || 'pass', pass: process.env.BITCOIND_PASS || 'pass',
host: process.env.BITCOIND_HOST || '127.0.0.1', host: process.env.BITCOIND_HOST || '127.0.0.1',
port: process.env.BITCOIND_PORT || '18332', port: process.env.BITCOIND_PORT || '18332',
p2p_port: process.env.BITCOIND_P2P_PORT || '18333',
disableAgent: true, disableAgent: true,
}, },
network: process.env.INSIGHT_NETWORK || 'testnet', network: process.env.INSIGHT_NETWORK || 'testnet',

View File

@ -1,8 +1,20 @@
'use strict'; 'use strict';
module.exports = { module.exports = {
db: 'mongodb://localhost/insight', db: 'mongodb://localhost/insight-test',
app: { app: {
name: 'Insight - Production' name: 'Insight - Prod'
} },
port: '3301',
bitcoind: {
protocol: process.env.BITCOIND_PROTO || 'http',
user: process.env.BITCOIND_USER || 'user',
pass: process.env.BITCOIND_PASS || 'pass',
host: process.env.BITCOIND_HOST || '127.0.0.1',
port: process.env.BITCOIND_PORT || '18332',
p2p_port: process.env.BITCOIND_P2P_PORT || '18333',
disableAgent: true,
},
network: 'testnet',
}; };

View File

@ -17,16 +17,16 @@ function spec() {
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:'; var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';
function HistoricSync(opts) { function HistoricSync() {
this.network = config.network === 'testnet' ? networks.testnet: networks.livenet; this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;
var genesisHashReversed = new Buffer(32); var genesisHashReversed = new Buffer(32);
this.network.genesisBlock.hash.copy(genesisHashReversed); this.network.genesisBlock.hash.copy(genesisHashReversed);
this.genesis = genesisHashReversed.reverse().toString('hex'); this.genesis = genesisHashReversed.reverse().toString('hex');
this.sync = new Sync(opts);
//available status: new / syncing / finished / aborted
this.status = 'new'; //available status: starting / syncing / finished / aborted
this.status = 'starting';
this.error = null; this.error = null;
this.syncPercentage = 0; this.syncPercentage = 0;
@ -54,8 +54,11 @@ function spec() {
HistoricSync.prototype.init = function(opts, cb) { HistoricSync.prototype.init = function(opts, cb) {
var self = this; var self = this;
self.rpc = new RpcClient(config.bitcoind);
self.opts = opts; self.opts = opts;
self.rpc = new RpcClient(config.bitcoind);
self.sync = new Sync(opts);
self.sync.init(opts, function(err) { self.sync.init(opts, function(err) {
if (err) { if (err) {
self.setError(err); self.setError(err);
@ -68,6 +71,7 @@ function spec() {
err = new Error(BAD_GEN_ERROR + config.network); err = new Error(BAD_GEN_ERROR + config.network);
self.setError(err); self.setError(err);
} }
if (err) self.setError(err);
return cb(err); return cb(err);
}); });
} }
@ -87,6 +91,7 @@ function spec() {
syncPercentage: this.syncPercentage, syncPercentage: this.syncPercentage,
skippedBlocks: this.skippedBlocks, skippedBlocks: this.skippedBlocks,
syncedBlocks: this.syncedBlocks, syncedBlocks: this.syncedBlocks,
error: this.error,
}; };
}; };
@ -94,7 +99,7 @@ function spec() {
var self = this; var self = this;
if (self.error) { if (self.error) {
p('ERROR:' + self.error); p('ERROR: ' + self.error);
} }
else { else {
self.syncPercentage = parseFloat(100 * self.syncedBlocks / self.blockChainHeight).toFixed(3); self.syncPercentage = parseFloat(100 * self.syncedBlocks / self.blockChainHeight).toFixed(3);
@ -107,7 +112,7 @@ function spec() {
} }
}; };
HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, opts, cb) { HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, scanOpts, cb) {
var self = this; var self = this;
// recursion end. // recursion end.
@ -171,7 +176,7 @@ function spec() {
if (blockInfo.result.prevblockhash != current.blockHash) { if (blockInfo.result.prevblockhash != current.blockHash) {
p("reorg?"); p("reorg?");
opts.prev = 1; scanOpts.prev = 1;
} }
return c(); return c();
} }
@ -189,7 +194,7 @@ function spec() {
self.status = 'syncing'; self.status = 'syncing';
} }
if ( (opts.upToExisting && existed && self.syncedBlocks >= self.blockChainHeight) || if ( (scanOpts.upToExisting && existed && self.syncedBlocks >= self.blockChainHeight) ||
(blockEnd && blockEnd === blockHash)) { (blockEnd && blockEnd === blockHash)) {
self.status = 'finished'; self.status = 'finished';
p('DONE. Found existing block: ', blockHash); p('DONE. Found existing block: ', blockHash);
@ -206,17 +211,17 @@ function spec() {
self.syncedBlocks++; self.syncedBlocks++;
// recursion // recursion
if (opts.prev && blockInfo.result.previousblockhash) if (scanOpts.prev && blockInfo.result.previousblockhash)
return self.getPrevNextBlock(blockInfo.result.previousblockhash, blockEnd, opts, cb); return self.getPrevNextBlock(blockInfo.result.previousblockhash, blockEnd, scanOpts, cb);
if (opts.next && blockInfo.result.nextblockhash) if (scanOpts.next && blockInfo.result.nextblockhash)
return self.getPrevNextBlock(blockInfo.result.nextblockhash, blockEnd, opts, cb); return self.getPrevNextBlock(blockInfo.result.nextblockhash, blockEnd, scanOpts, cb);
} }
return cb(err); return cb(err);
}); });
}; };
HistoricSync.prototype.importHistory = function(opts, next) { HistoricSync.prototype.importHistory = function(scanOpts, next) {
var self = this; var self = this;
var retry_secs = 2; var retry_secs = 2;
@ -225,7 +230,7 @@ function spec() {
async.series([ async.series([
function(cb) { function(cb) {
if (opts.destroy) { if (scanOpts.destroy) {
p('Deleting DB...'); p('Deleting DB...');
return self.sync.destroy(cb); return self.sync.destroy(cb);
} }
@ -233,7 +238,7 @@ function spec() {
}, },
// We are not using getBestBlockHash, because is not available in all clients // We are not using getBestBlockHash, because is not available in all clients
function(cb) { function(cb) {
if (!opts.reverse) return cb(); if (!scanOpts.reverse) return cb();
self.rpc.getBlockCount(function(err, res) { self.rpc.getBlockCount(function(err, res) {
if (err) return cb(err); if (err) return cb(err);
@ -242,19 +247,17 @@ function spec() {
}); });
}, },
function(cb) { function(cb) {
if (!opts.reverse) return cb(); if (!scanOpts.reverse) return cb();
self.rpc.getBlockHash(self.blockChainHeight, function(err, res) { self.rpc.getBlockHash(self.blockChainHeight, function(err, res) {
if (err) return cb(err); if (err) return cb(err);
lastBlock = res.result; lastBlock = res.result;
return cb(); return cb();
}); });
}, },
function(cb) { function(cb) {
if (opts.upToExisting) { if (scanOpts.upToExisting) {
// should be isOrphan = true or null to be more accurate. // should be isOrphan = true or null to be more accurate.
Block.count({ Block.count({
isOrphan: null isOrphan: null
@ -266,25 +269,28 @@ function spec() {
return cb(); return cb();
}); });
} }
else {
return cb();
}
}, },
], function(err) { ], function(err) {
var start, end; var start, end;
function sync() { function sync() {
if (opts.reverse) { if (scanOpts.reverse) {
start = lastBlock; start = lastBlock;
end = self.genesis; end = self.genesis;
opts.prev = true; scanOpts.prev = true;
} }
else { else {
start = self.genesis; start = self.genesis;
end = null; end = null;
opts.next = true; scanOpts.next = true;
} }
p('Starting from: ', start); p('Starting from: ', start);
p(' to : ', end); p(' to : ', end);
p(' opts: ', JSON.stringify(opts)); p(' scanOpts: ', JSON.stringify(scanOpts));
self.getPrevNextBlock(start, end, opts, function(err) { self.getPrevNextBlock(start, end, scanOpts, function(err) {
if (err && err.message.match(/ECONNREFUSED/)) { if (err && err.message.match(/ECONNREFUSED/)) {
setTimeout(function() { setTimeout(function() {
p('Retrying in %d secs', retry_secs); p('Retrying in %d secs', retry_secs);
@ -336,12 +342,12 @@ function spec() {
p('Genesis block found. Syncing upto known blocks.'); p('Genesis block found. Syncing upto known blocks.');
} }
var opts = { var scanOpts = {
reverse: true, reverse: true,
upToExisting: b ? true: false, upToExisting: b ? true: false,
}; };
return self.importHistory(opts, next); return self.importHistory(scanOpts, next);
}); });
}; };

View File

@ -7,47 +7,42 @@ function spec() {
var coinUtil = require('bitcore/util/util'); var coinUtil = require('bitcore/util/util');
var Sync = require('./Sync').class(); var Sync = require('./Sync').class();
var Peer = require('bitcore/Peer').class(); var Peer = require('bitcore/Peer').class();
var config = require('../config/config');
var peerdb_fn = 'peerdb.json'; var peerdb_fn = 'peerdb.json';
function PeerSync() {} function PeerSync() {}
PeerSync.prototype.init = function(config, cb) { PeerSync.prototype.init = function(opts, cb) {
if (!config) config = {}; if (!opts) opts = {};
var network = config && (config.network || 'testnet'); var network = opts && (opts.network || 'testnet');
this.verbose = config.verbose; this.verbose = opts.verbose;
this.peerdb = undefined; this.peerdb = undefined;
this.sync = new Sync({ this.sync = new Sync({
networkName: network networkName: network
}); });
this.PeerManager = require('bitcore/PeerManager').createClass({ this.PeerManager = require('bitcore/PeerManager').createClass({
config: { opts: {
network: network network: network
} }
}); });
this.peerman = new this.PeerManager(); this.peerman = new this.PeerManager();
this.load_peers(); this.load_peers();
this.sync.init(config, function() { this.sync.init(opts, function() {
return cb(); return cb();
}); });
}; };
PeerSync.prototype.load_peers = function() { PeerSync.prototype.load_peers = function() {
try { this.peerdb = [{
this.peerdb = JSON.parse(fs.readFileSync(peerdb_fn)); ipv4: config.bitcoind.host,
} catch(d) { port: config.bitcoind.p2p_port
console.warn('Unable to read peer db', peerdb_fn, 'creating new one.'); }];
this.peerdb = [{
ipv4: '127.0.0.1',
port: 18333
},
];
fs.writeFileSync(peerdb_fn, JSON.stringify(this.peerdb)); fs.writeFileSync(peerdb_fn, JSON.stringify(this.peerdb));
}
}; };
PeerSync.prototype.handle_inv = function(info) { PeerSync.prototype.handle_inv = function(info) {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
angular.module('insight.status').controller('StatusController', angular.module('insight.status').controller('StatusController',
function($scope, $routeParams, $location, $rootScope, Global, Status, Sync, getSocket) { function($scope, $routeParams, $location, Global, Status, Sync, getSocket) {
$scope.global = Global; $scope.global = Global;
$scope.getStatus = function(q) { $scope.getStatus = function(q) {
@ -9,16 +9,11 @@ function($scope, $routeParams, $location, $rootScope, Global, Status, Sync, getS
q: 'get' + q q: 'get' + q
}, },
function(d) { function(d) {
$rootScope.infoError = null; $scope.loaded = 1;
angular.extend($scope, d); angular.extend($scope, d);
}, },
function(e) { function(e) {
if (e.status === 503) { $scope.error = 'API ERROR: ' + e.data;
$rootScope.infoError = 'Backend Error. ' + e.data;
}
else {
$rootScope.infoError = 'Unknown error:' + e.data;
}
}); });
}; };
@ -32,16 +27,15 @@ function($scope, $routeParams, $location, $rootScope, Global, Status, Sync, getS
_onSyncUpdate(sync); _onSyncUpdate(sync);
}, },
function(e) { function(e) {
$scope.sync = { error: 'Could not get sync information' + e }; var err = 'Could not get sync information' + e.toString();
$scope.sync = { error: err };
}); });
}; };
var socket = getSocket($scope); var socket = getSocket($scope);
socket.emit('subscribe', 'sync'); socket.emit('subscribe', 'sync');
socket.on('status', function(sync) { socket.on('status', function(sync) {
console.log('[status.js.55::] sync status update received!');
_onSyncUpdate(sync); _onSyncUpdate(sync);
}); });
}); });

View File

@ -0,0 +1,4 @@
<span class="text-warning" data-ng-show="!loaded && !error">Loading...</span>
<span class="text-danger" data-ng-show="error">{{error}}</span>

View File

@ -1,4 +1,4 @@
<section data-ng-controller="StatusController"> <section>
<div class="page-header"> <div class="page-header">
<h1> <h1>
Application Status Application Status
@ -8,11 +8,11 @@
<div class="col-md-9"> <div class="col-md-9">
<h4>Sync Status</h4> <h4>Sync Status</h4>
<table class="table" data-ng-init="getSync()"> <table class="table" data-ng-controller="StatusController" data-ng-init="getSync()">
<tbody> <tbody>
<tr data-ng-show="sync.error"> <thead>
<td colspan="2"> <span class="text-danger"> {{sync.error}} </span> <span class="text-danger" data-ng-show="sync && sync.error">{{sync.error}}</span>
</tr> </thead>
<tr> <tr>
<td>Sync Progress</td> <td>Sync Progress</td>
<td> <td>
@ -22,6 +22,11 @@
</div> </div>
</div> </div>
</tr> </tr>
<tr>
<td>Current Sync Status</td>
<td class="text-right">{{sync.status}}</td>
</tr>
<tr> <tr>
<td>Initial Block Chain Height</td> <td>Initial Block Chain Height</td>
<td class="text-right">{{sync.blockChainHeight}}</td> <td class="text-right">{{sync.blockChainHeight}}</td>
@ -36,14 +41,9 @@
</table> </table>
<h4>Transaction Output Set Information</h4> <h4>Transaction Output Set Information</h4>
<table class="table" data-ng-init="getStatus('TxOutSetInfo')"> <table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('TxOutSetInfo')">
<thead data-ng-include src="'/views/infoStatus.html'"> </thead>
<tbody> <tbody>
<tr data-ng-show="!txoutsetinfo &amp;&amp; !infoError">
<td colspan="2" class="text-center">Loading...</td>
</tr>
<tr data-ng-show="infoError">
<td colspan="2" class="text-danger">{{infoError}}</td>
</tr>
<tr> <tr>
<td>Height</td> <td>Height</td>
<td class="text-right"><a href="/#!/block-index/{{txoutsetinfo.height}}">{{txoutsetinfo.height}}</a></td> <td class="text-right"><a href="/#!/block-index/{{txoutsetinfo.height}}">{{txoutsetinfo.height}}</a></td>
@ -76,16 +76,10 @@
</table> </table>
<h4>Last Block</h4> <h4>Last Block</h4>
<table class="table" data-ng-init="getStatus('LastBlockHash')"> <table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('LastBlockHash')">
<tbody> <thead data-ng-include src="'/views/infoStatus.html'"> </thead>
<tr data-ng-show="!lastblockhash &amp;&amp; !infoError">
<td colspan="2" class="text-center">Loading...</td>
</tr>
<tr data-ng-show="infoError">
<td colspan="2" class="text-danger">{{infoError}}</td>
</tr>
<tr> <tr>
<td>Last block hash</td> <td>Last Block Hash</td>
<td class="text-right"><a href="/#!/block/{{lastblockhash}}">{{lastblockhash}}</a></td> <td class="text-right"><a href="/#!/block/{{lastblockhash}}">{{lastblockhash}}</a></td>
</tr> </tr>
</tbody> </tbody>
@ -95,12 +89,9 @@
<div class="col-md-3"> <div class="col-md-3">
<div class="col-gray"> <div class="col-gray">
<h4>Bitcoin node information</h4> <h4>Bitcoin node information</h4>
<table class="table" data-ng-init="getStatus('Info')"> <table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('Info')">
<thead data-ng-include src="'/views/infoStatus.html'"> </thead>
<tbody> <tbody>
<tr data-ng-show="!info &amp;&amp; !infoError">
<td colspan="2" class="text-center">Loading...
<tr data-ng-show="infoError">
<td colspan="2" class="text-danger">{{infoError}}
<tr> <tr>
<td>Version</td> <td>Version</td>
<td class="text-right">{{info.version}}</td> <td class="text-right">{{info.version}}</td>
@ -109,14 +100,6 @@
<td>Protocol version</td> <td>Protocol version</td>
<td class="text-right">{{info.protocolversion}}</td> <td class="text-right">{{info.protocolversion}}</td>
</tr> </tr>
<tr>
<td>Wallet version</td>
<td class="text-right">{{info.walletversion}}</td>
</tr>
<tr>
<td>Balance (BTC)</td>
<td class="text-right">{{info.balance}}</td>
</tr>
<tr> <tr>
<td>Blocks</td> <td>Blocks</td>
<td class="text-right"><a href="/#!/block-index/{{info.blocks}}">{{info.blocks}}</a></td> <td class="text-right"><a href="/#!/block-index/{{info.blocks}}">{{info.blocks}}</a></td>
@ -161,15 +144,9 @@
</table> </table>
<h4>Difficulty</h4> <h4>Difficulty</h4>
<table class="table" data-ng-init="getStatus('Difficulty')"> <table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('Difficulty')">
<tbody> <thead data-ng-include src="'/views/infoStatus.html'"> </thead>
<tr data-ng-show="!difficulty &amp;&amp; !infoError"> <tbody
<td colspan="2" class="text-center">Loading...</td>
</tr>
<tr data-ng-show="infoError">
<td colspan="2" class="text-danger">{{infoError}}</td>
</tr>
<tr> <tr>
<td>Mining Difficulty</td> <td>Mining Difficulty</td>
<td>{{difficulty}}</td> <td>{{difficulty}}</td>

View File

@ -18,9 +18,7 @@ program
.option('-U --uptoexisting', 'Sync only until an existing block is found', 0) .option('-U --uptoexisting', 'Sync only until an existing block is found', 0)
.parse(process.argv); .parse(process.argv);
var historicSync = new HistoricSync({ var historicSync = new HistoricSync();
networkName: program.network
});
/* TODO: Sure? /* TODO: Sure?
if (program.remove) { if (program.remove) {
@ -32,6 +30,7 @@ async.series([
historicSync.init(program, cb); historicSync.init(program, cb);
}, },
function(cb) { function(cb) {
if (program.smart) { if (program.smart) {
historicSync.smartImport(cb); historicSync.smartImport(cb);
} }
@ -47,7 +46,7 @@ async.series([
function(err) { function(err) {
historicSync.close(); historicSync.close();
if (err) { if (err) {
console.log('CRITICAL ERROR: ', err); console.log('CRITICAL ERROR: ', historicSync.info());
} }
else { else {
console.log('Finished.\n Status:\n', historicSync.info()); console.log('Finished.\n Status:\n', historicSync.info());