more chain refactoring.
This commit is contained in:
parent
1f3d2f4b96
commit
c580557993
@ -199,7 +199,7 @@ Chain.prototype._init = function _init() {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self._preload(function(err) {
|
||||
self.preload(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
@ -219,7 +219,7 @@ Chain.prototype._init = function _init() {
|
||||
|
||||
bcoin.profiler.snapshot();
|
||||
|
||||
self._getInitialState(function(err) {
|
||||
self.getInitialState(function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
@ -268,6 +268,12 @@ Chain.prototype.destroy = function destroy(callback) {
|
||||
this.db.close(utils.ensure(callback));
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke mutex lock.
|
||||
* @private
|
||||
* @returns {Function} unlock
|
||||
*/
|
||||
|
||||
Chain.prototype._lock = function _lock(func, args, force) {
|
||||
return this.locker.lock(func, args, force);
|
||||
};
|
||||
@ -282,13 +288,13 @@ Chain.prototype._lock = function _lock(func, args, force) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Chain.prototype._preload = function _preload(callback) {
|
||||
Chain.prototype.preload = function preload(callback) {
|
||||
var self = this;
|
||||
var url = 'https://headers.electrum.org/blockchain_headers';
|
||||
var buf, height, stream;
|
||||
var request = require('./http/request');
|
||||
var locker = new bcoin.locker();
|
||||
var flushed = 0;
|
||||
var chunks, size, height, stream, ended;
|
||||
|
||||
if (!this.options.preload)
|
||||
return callback();
|
||||
@ -301,21 +307,6 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
|
||||
bcoin.debug('Loading %s.', url);
|
||||
|
||||
function parseHeader(data) {
|
||||
var p = bcoin.reader(data, true);
|
||||
var hash = utils.hash256(p.readBytes(80)).toString('hex');
|
||||
p.seek(-80);
|
||||
return {
|
||||
hash: hash,
|
||||
version: p.readU32(), // Technically signed
|
||||
prevBlock: p.readHash('hex'),
|
||||
merkleRoot: p.readHash('hex'),
|
||||
ts: p.readU32(),
|
||||
bits: p.readU32(),
|
||||
nonce: p.readU32()
|
||||
};
|
||||
}
|
||||
|
||||
function save(entry, header) {
|
||||
var unlock = locker.lock(save, [entry]);
|
||||
if (!unlock)
|
||||
@ -331,7 +322,7 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
if ((++flushed % 50000) === 0)
|
||||
utils.print('Flushed %d headers to DB.', flushed);
|
||||
|
||||
if (locker.jobs.length === 0 && save.ended)
|
||||
if (locker.jobs.length === 0 && ended)
|
||||
return callback();
|
||||
|
||||
unlock();
|
||||
@ -344,13 +335,11 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
|
||||
stream = request({ method: 'GET', uri: url });
|
||||
height = 0;
|
||||
buf = {
|
||||
data: [],
|
||||
size: 0
|
||||
};
|
||||
chunks = [];
|
||||
size = 0;
|
||||
|
||||
stream.on('response', function(res) {
|
||||
var height = +res.headers['content-length'] / 80 | 0;
|
||||
var height = Math.floor(res.headers['content-length'] / 80);
|
||||
if (res.statusCode >= 400) {
|
||||
stream.destroy();
|
||||
return callback(new Error('Bad response code: ' + res.statusCode));
|
||||
@ -369,60 +358,54 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
|
||||
stream.on('data', function(data) {
|
||||
var blocks = [];
|
||||
var need = 80 - buf.size;
|
||||
var need = 80 - size;
|
||||
var i, lastEntry, block, entry;
|
||||
|
||||
while (data.length >= need) {
|
||||
buf.data.push(data.slice(0, need));
|
||||
blocks.push(Buffer.concat(buf.data));
|
||||
buf.data.length = 0;
|
||||
buf.size = 0;
|
||||
chunks.push(data.slice(0, need));
|
||||
blocks.push(Buffer.concat(chunks));
|
||||
chunks.length = 0;
|
||||
data = data.slice(need);
|
||||
need = 80 - buf.size;
|
||||
need = 80;
|
||||
}
|
||||
|
||||
if (data.length > 0) {
|
||||
assert(data.length < 80);
|
||||
buf.data.push(data);
|
||||
buf.size += data.length;
|
||||
chunks.push(data);
|
||||
size += data.length;
|
||||
}
|
||||
|
||||
for (i = 0; i < blocks.length; i++) {
|
||||
data = blocks[i];
|
||||
block = blocks[i];
|
||||
|
||||
try {
|
||||
data = parseHeader(data);
|
||||
block = bcoin.headers.fromAbbr(block);
|
||||
} catch (e) {
|
||||
stream.destroy();
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
data.height = height;
|
||||
block.setHeight(height);
|
||||
|
||||
// Make sure the genesis block is correct.
|
||||
if (data.height === 0 && data.hash !== self.network.genesis.hash) {
|
||||
if (block.height === 0 && !self.isGenesis(block)) {
|
||||
stream.destroy();
|
||||
return callback(new Error('Bad genesis block.'));
|
||||
}
|
||||
|
||||
// Do some paranoid checks.
|
||||
if (lastEntry && data.prevBlock !== lastEntry.hash) {
|
||||
if (lastEntry && block.prevBlock !== lastEntry.hash) {
|
||||
stream.destroy();
|
||||
return callback(new Error('Corrupt headers.'));
|
||||
}
|
||||
|
||||
// Create headers object for validation.
|
||||
block = new bcoin.headers(data);
|
||||
|
||||
// Verify the block headers. We don't want to
|
||||
// trust an external centralized source completely.
|
||||
if (!block.verifyHeaders()) {
|
||||
if (!block.verify()) {
|
||||
stream.destroy();
|
||||
return callback(new Error('Bad headers.'));
|
||||
}
|
||||
|
||||
block.setHeight(height);
|
||||
|
||||
// Create a chain entry.
|
||||
entry = bcoin.chainentry.fromBlock(self, block, lastEntry);
|
||||
|
||||
@ -438,7 +421,7 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
save.ended = true;
|
||||
ended = true;
|
||||
if (!locker.busy && locker.jobs.length === 0)
|
||||
return callback();
|
||||
});
|
||||
@ -453,18 +436,18 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Chain.prototype._verifyContext = function _verifyContext(block, prev, callback) {
|
||||
Chain.prototype.verifyContext = function verifyContext(block, prev, callback) {
|
||||
var self = this;
|
||||
|
||||
this._verify(block, prev, function(err, state) {
|
||||
this.verify(block, prev, function(err, state) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self._checkDuplicates(block, prev, function(err) {
|
||||
self.checkDuplicates(block, prev, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
self._checkInputs(block, prev, state, function(err, view) {
|
||||
self.checkInputs(block, prev, state, function(err, view) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -474,6 +457,12 @@ Chain.prototype._verifyContext = function _verifyContext(block, prev, callback)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a block is the genesis block.
|
||||
* @param {Block} block
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Chain.prototype.isGenesis = function isGenesis(block) {
|
||||
return block.hash('hex') === this.network.genesis.hash;
|
||||
};
|
||||
@ -489,7 +478,7 @@ Chain.prototype.isGenesis = function isGenesis(block) {
|
||||
* [{@link VerifyError}, {@link VerifyFlags}].
|
||||
*/
|
||||
|
||||
Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
Chain.prototype.verify = function verify(block, prev, callback) {
|
||||
var self = this;
|
||||
var ret = {};
|
||||
var height, ts, i, tx, medianTime, commitmentHash;
|
||||
@ -731,7 +720,7 @@ Chain.prototype.getDeployments = function getDeployments(block, prev, ancestors,
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) {
|
||||
Chain.prototype.checkDuplicates = function checkDuplicates(block, prev, callback) {
|
||||
var self = this;
|
||||
var height = prev.height + 1;
|
||||
|
||||
@ -743,7 +732,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
|
||||
if (this.network.block.bip34height === -1
|
||||
|| height <= this.network.block.bip34height) {
|
||||
return this._findDuplicates(block, prev, callback);
|
||||
return this.findDuplicates(block, prev, callback);
|
||||
}
|
||||
|
||||
this.db.get(this.network.block.bip34height, function(err, entry) {
|
||||
@ -756,7 +745,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
if (entry && entry.hash === self.network.block.bip34hash)
|
||||
return callback();
|
||||
|
||||
return self._findDuplicates(block, prev, callback);
|
||||
return self.findDuplicates(block, prev, callback);
|
||||
});
|
||||
};
|
||||
|
||||
@ -771,7 +760,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Chain.prototype._findDuplicates = function _findDuplicates(block, prev, callback) {
|
||||
Chain.prototype.findDuplicates = function findDuplicates(block, prev, callback) {
|
||||
var self = this;
|
||||
var height = prev.height + 1;
|
||||
|
||||
@ -815,7 +804,7 @@ Chain.prototype._findDuplicates = function _findDuplicates(block, prev, callback
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Chain.prototype._checkInputs = function _checkInputs(block, prev, state, callback) {
|
||||
Chain.prototype.checkInputs = function checkInputs(block, prev, state, callback) {
|
||||
var self = this;
|
||||
var height = prev.height + 1;
|
||||
var scriptCheck = true;
|
||||
@ -927,6 +916,13 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, state, callbac
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the cached height for a hash if present.
|
||||
* @private
|
||||
* @param {Hash} hash
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Chain.prototype._getCachedHeight = function _getCachedHeight(hash) {
|
||||
if (this.db.hasCache(hash))
|
||||
return this.db.getCache(hash).height;
|
||||
@ -942,7 +938,7 @@ Chain.prototype._getCachedHeight = function _getCachedHeight(hash) {
|
||||
* @param {Function} callback - Returns [{@link Error}, {@link ChainEntry}].
|
||||
*/
|
||||
|
||||
Chain.prototype._findFork = function _findFork(fork, longer, callback) {
|
||||
Chain.prototype.findFork = function findFork(fork, longer, callback) {
|
||||
(function find() {
|
||||
if (fork.hash === longer.hash)
|
||||
return callback(null, fork);
|
||||
@ -993,11 +989,11 @@ Chain.prototype._findFork = function _findFork(fork, longer, callback) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Chain.prototype._reorganize = function _reorganize(entry, block, callback) {
|
||||
Chain.prototype.reorganize = function reorganize(entry, block, callback) {
|
||||
var self = this;
|
||||
var tip = this.tip;
|
||||
|
||||
return this._findFork(tip, entry, function(err, fork) {
|
||||
return this.findFork(tip, entry, function(err, fork) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -1140,7 +1136,7 @@ Chain.prototype.connect = function connect(entry, callback) {
|
||||
|
||||
assert(prev);
|
||||
|
||||
self._verifyContext(block, prev, function(err, view) {
|
||||
self.verifyContext(block, prev, function(err, view) {
|
||||
if (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
self.invalid[entry.hash] = true;
|
||||
@ -1185,7 +1181,7 @@ Chain.prototype.connect = function connect(entry, callback) {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Chain.prototype._setBestChain = function _setBestChain(entry, block, prev, callback) {
|
||||
Chain.prototype.setBestChain = function setBestChain(entry, block, prev, callback) {
|
||||
var self = this;
|
||||
|
||||
function done(err) {
|
||||
@ -1195,7 +1191,7 @@ Chain.prototype._setBestChain = function _setBestChain(entry, block, prev, callb
|
||||
// Do "contextual" verification on our block
|
||||
// now that we're certain its previous
|
||||
// block is in the chain.
|
||||
self._verifyContext(block, prev, function(err, view) {
|
||||
self.verifyContext(block, prev, function(err, view) {
|
||||
if (err) {
|
||||
// Couldn't verify block.
|
||||
// Revert the height.
|
||||
@ -1248,7 +1244,7 @@ Chain.prototype._setBestChain = function _setBestChain(entry, block, prev, callb
|
||||
// A higher fork has arrived.
|
||||
// Time to reorganize the chain.
|
||||
bcoin.debug('WARNING: Reorganizing chain.');
|
||||
return this._reorganize(entry, block, done);
|
||||
return this.reorganize(entry, block, done);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1574,7 +1570,7 @@ Chain.prototype.add = function add(block, callback, force) {
|
||||
}
|
||||
|
||||
// Attempt to add block to the chain index.
|
||||
self._setBestChain(entry, block, prev, function(err) {
|
||||
self.setBestChain(entry, block, prev, function(err) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
@ -2314,14 +2310,12 @@ Chain.prototype.computeBlockVersion = function computeBlockVersion(prev, callbac
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function to test whether segwit is active at any
|
||||
* given time. Since segwit affects almost all of bitcoin, it
|
||||
* is one deployment that needs to be checked frequently.
|
||||
* Get the initial deployment state of the chain. Called on load.
|
||||
* @private
|
||||
* @param {Function} callback - Returns [Error, Boolean].
|
||||
* @param {Function} callback - Returns [Error, {@link DeploymentState}].
|
||||
*/
|
||||
|
||||
Chain.prototype._getInitialState = function _getInitialState(callback) {
|
||||
Chain.prototype.getInitialState = function getInitialState(callback) {
|
||||
var self = this;
|
||||
|
||||
if (this.segwitActive != null)
|
||||
@ -2330,7 +2324,7 @@ Chain.prototype._getInitialState = function _getInitialState(callback) {
|
||||
if (!this.tip)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
return this.tip.getPrevious(function(err, prev) {
|
||||
this.tip.getPrevious(function(err, prev) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -2351,7 +2345,7 @@ Chain.prototype._getInitialState = function _getInitialState(callback) {
|
||||
self.csvActive = state.hasCSV();
|
||||
self.segwitActive = state.hasWitness();
|
||||
|
||||
return callback();
|
||||
return callback(null, state);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -129,6 +129,38 @@ Headers.fromRaw = function fromRaw(data, enc) {
|
||||
return new Headers().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
Headers.prototype.fromAbbr = function fromAbbr(data) {
|
||||
var p = bcoin.reader(data);
|
||||
|
||||
this.version = p.readU32(); // Technically signed
|
||||
this.prevBlock = p.readHash('hex');
|
||||
this.merkleRoot = p.readHash('hex');
|
||||
this.ts = p.readU32();
|
||||
this.bits = p.readU32();
|
||||
this.nonce = p.readU32();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate headers from serialized data.
|
||||
* @param {Buffer} data
|
||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
||||
* @returns {Headers}
|
||||
*/
|
||||
|
||||
Headers.fromAbbr = function fromAbbr(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new Headers().fromAbbr(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate headers from a chain entry.
|
||||
* @param {ChainEntry} entry
|
||||
|
||||
Loading…
Reference in New Issue
Block a user