add preload from electrum.org. check for blockchain corruption.

This commit is contained in:
Christopher Jeffrey 2016-02-08 20:46:57 -08:00
parent adb304748f
commit 36c3d72021

View File

@ -6,6 +6,7 @@
var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var request = require('request');
var bcoin = require('../bcoin');
var bn = require('bn.js');
@ -95,42 +96,137 @@ Chain.prototype._init = function _init() {
utils.debug('Chain is loading.');
utils.nextTick(function() {
var count = self.db.count();
var lastEntry;
var i = 1;
this.preload(function(start) {
utils.nextTick(function() {
var count = self.db.count();
var i = start || 1;
var lastEntry;
function done(height) {
if (height != null) {
utils.debug(
'Blockchain is corrupt after height %d. Resetting.',
height);
self.resetHeight(height);
} else {
utils.debug('Chain successfully loaded.');
function done(height) {
if (height != null) {
utils.debug(
'Blockchain is corrupt after height %d. Resetting.',
height);
self.resetHeight(height);
} else {
utils.debug('Chain successfully loaded.');
}
self.loading = false;
self.emit('load');
}
self.loading = false;
self.emit('load');
(function next() {
if (i >= count)
return done();
self.db.getAsync(i, function(err, entry) {
if (err)
throw err;
// Do some paranoid checks.
if (lastEntry && entry.prevBlock !== lastEntry.hash)
return done(Math.max(0, i - 2));
lastEntry = entry;
self._saveEntry(entry);
i += 1;
next();
});
})();
});
});
};
Chain.prototype.preload = function preload(callback) {
var self = this;
var url = 'https://headers.electrum.org/blockchain_headers';
var chainHeight, buf, height, stream;
if (this.options.preload === false)
return callback();
utils.debug('Loading %s', url);
stream = request.get(url);
chainHeight = this.db.count() - 1;
height = 0;
buf = {
data: [],
size: 0
};
stream.on('response', function(res) {
if (res.statusCode >= 400) {
utils.debug('Could not load electrum headers');
utils.debug('Status: %d', res.statusCode);
stream.destroy();
return callback();
}
});
stream.on('error', function(err) {
utils.debug('Could not load electrum headers');
utils.debug(err.stack + '');
self.resetHeight(Math.max(0, height - 2));
return callback(Math.max(0, height - 2));
});
stream.on('data', function(data) {
var blocks = [];
var need = 80 - buf.size;
var i, lastEntry;
while (data.length >= need) {
buf.data.push(data.slice(0, need));
blocks.push(Buffer.concat(buf.data));
buf.data.length = 0;
buf.size = 0;
data = data.slice(need);
need = 80 - buf.size;
}
(function next() {
if (i >= count)
return done();
if (data.length > 0) {
assert(data.length < 80);
buf.data.push(data);
buf.size += data.length;
}
self.db.getAsync(i, function(err, entry) {
if (err)
throw err;
if (blocks.length === 0)
return;
// Do some paranoid checks.
if (lastEntry && entry.prevBlock !== lastEntry.hash)
return done(Math.max(0, i - 2));
blocks.forEach(function(data) {
var entry = bcoin.chainblock.fromRaw(self, height, data);
self._saveEntry(entry);
lastEntry = entry;
i += 1;
next();
});
})();
// Do some paranoid checks.
if (lastEntry && entry.prevBlock !== lastEntry.hash) {
utils.debug('Electrum headers are corrupt. Resetting.');
stream.destroy();
self.resetHeight(Math.max(0, height - 2));
return callback(Math.max(0, height - 2));
}
lastEntry = entry;
delete entry.chainwork;
entry.chainwork = entry.getChainwork();
// Skip the genesis block in case it ends up being corrupt
if (height === 0) {
height++;
return;
}
self._saveEntry(entry, height > chainHeight);
height++;
if ((height + 1) % 50000 === 0)
utils.debug('Received %d headers from electrum.org.', height + 1);
});
});
stream.on('end', function() {
return callback(null, height + 1);
});
};