blockdb reset height.

This commit is contained in:
Christopher Jeffrey 2016-02-16 17:12:06 -08:00
parent 997f0acbb0
commit 0191acd530
4 changed files with 270 additions and 23 deletions

View File

@ -24,6 +24,7 @@ if (!bcoin.isBrowser) {
bcoin.fs = require('f' + 's'); bcoin.fs = require('f' + 's');
bcoin.crypto = require('cry' + 'pto'); bcoin.crypto = require('cry' + 'pto');
bcoin.net = require('n' + 'et'); bcoin.net = require('n' + 'et');
bcoin.cp = require('child_' + 'process');
} }
bcoin.elliptic = elliptic; bcoin.elliptic = elliptic;

View File

@ -63,6 +63,21 @@ function BlockDB(options) {
inherits(BlockDB, EventEmitter); inherits(BlockDB, EventEmitter);
BlockDB.prototype.close = function close(callback) {
var self = this;
this.index.close(function(err) {
if (err)
return callback(err);
self.data.closeAsync(function(err) {
if (err)
return callback(err);
return callback();
});
});
};
BlockDB.prototype.migrate = function migrate(blockSize, compression, callback) { BlockDB.prototype.migrate = function migrate(blockSize, compression, callback) {
var options, db, pending, stream, total, done; var options, db, pending, stream, total, done;
@ -251,14 +266,23 @@ BlockDB.prototype.removeBlock = function removeBlock(hash, callback) {
batch = self.index.batch(); batch = self.index.batch();
batch.del('b/b/' + hash); if (typeof hash === 'string')
assert(block.hash('hex') === hash);
batch.del('b/b/' + block.hash('hex'));
batch.del('b/h/' + block.height); batch.del('b/h/' + block.height);
function done() { function done() {
batch.write(function(err) { batch.write(function(err) {
if (err) if (err)
return callback(err); return callback(err);
return callback(null, block); // TODO: Add check to make sure we
// can ONLY remove the last block.
self.data.truncateAsync(block._fileOffset, function(err) {
if (err)
return callback(err);
return callback(null, block);
});
}); });
} }
@ -810,6 +834,29 @@ BlockDB.prototype.getBlock = function getBlock(hash, callback) {
}); });
}; };
BlockDB.prototype.hasBlock = function hasBlock(hash, callback) {
var self = this;
var id = 'b/b/' + value;
if (typeof hash === 'number')
id = 'b/h/' + value;
this.index.get(id, function(err, record) {
if (err && err.type !== 'NotFoundError')
return callback(err);
if (!record)
return callback(null, false);
record = self.parseOffset(record);
if (self.data.size < record.offset + record.size)
return callback(null, false);
return callback(null, true);
});
};
BlockDB.prototype.hasCoin = function hasCoin(hash, index, callback) { BlockDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
var id = 'u/t/' + hash + '/' + index; var id = 'u/t/' + hash + '/' + index;
@ -817,7 +864,15 @@ BlockDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
if (err && err.type !== 'NotFoundError') if (err && err.type !== 'NotFoundError')
return callback(err); return callback(err);
return callback(null, record ? true : false); if (!record)
return callback(null, false);
record = self.parseOffset(record);
if (self.data.size < record.offset + record.size)
return callback(null, false);
return callback(null, true);
}); });
}; };
@ -828,7 +883,15 @@ BlockDB.prototype.hasTX = function hasTX(hash, callback) {
if (err && err.type !== 'NotFoundError') if (err && err.type !== 'NotFoundError')
return callback(err); return callback(err);
return callback(null, record ? true : false); if (!record)
return callback(null, false);
record = self.parseOffset(record);
if (self.data.size < record.offset + record.size)
return callback(null, false);
return callback(null, true);
}); });
}; };
@ -841,6 +904,57 @@ BlockDB.prototype.isSpent = function isSpent(hash, index, callback) {
}); });
}; };
BlockDB.prototype.getHeight = function getHeight(callback) {
var self = this;
var maxHeight = -1;
var stream;
callback = utils.asyncify(callback);
stream = this.index.createKeyStream({
start: 'b/h',
end: 'b/h~'
});
stream.on('data', function(data) {
var parts = data.key.split('/').slice(2);
var height = +parts[0];
if (height > maxHeight)
maxHeight = height;
});
stream.on('error', function(err) {
return callback(err);
});
stream.on('end', function() {
return callback(null, maxHeight);
});
};
BlockDB.prototype.resetHeight = function resetHeight(height, callback) {
var self = this;
this.getHeight(function(err, currentHeight) {
if (err)
return callback(err);
if (currentHeight < height)
return callback(new Error('Cannot reset to height ' + height));
(function next() {
if (height === currentHeight)
return callback();
self.removeBlock(height, function(err, block) {
if (err)
return callback(err);
height--;
next();
});
})();
});
};
/** /**
* BlockData * BlockData
*/ */
@ -888,7 +1002,34 @@ BlockData.prototype._init = function _init() {
this.fd = fs.openSync(this.file, 'r+'); this.fd = fs.openSync(this.file, 'r+');
}; };
BlockData.prototype._malloc = function(size) { BlockData.prototype.closeSync = function closeSync() {
if (!bcoin.fs) {
this.ramdisk = null;
return;
}
fs.closeSync(this.fd);
this.fd = null;
};
BlockData.prototype.closeAsync = function closeAsync(callback) {
var self = this;
callback = utils.asyncify(callback);
if (!bcoin.fs) {
this.ramdisk = null;
return callback();
}
fs.close(this.fd, function(err) {
if (err)
return callback(err);
self.fd = null;
return callback();
});
};
BlockData.prototype._malloc = function _malloc(size) {
if (size > 500) if (size > 500)
return new Buffer(size); return new Buffer(size);
@ -903,7 +1044,7 @@ BlockData.prototype._malloc = function(size) {
return this._bufferPool[size]; return this._bufferPool[size];
}; };
BlockData.prototype._free = function(buf) { BlockData.prototype._free = function _free(buf) {
if (this._bufferPool.used[buf.length] === buf) { if (this._bufferPool.used[buf.length] === buf) {
assert(this._bufferPool[buf.length] === buf); assert(this._bufferPool[buf.length] === buf);
delete this._bufferPool.used[buf.length]; delete this._bufferPool.used[buf.length];
@ -967,15 +1108,34 @@ BlockData.prototype.saveAsync = function saveAsync(data, callback) {
}); });
}; };
BlockData.prototype.truncate = function truncate(size) { BlockData.prototype.truncateSync = function truncateSync(size) {
this.size = size;
if (!bcoin.fs) { if (!bcoin.fs) {
this.ramdisk.truncate(this.size); this.ramdisk.truncate(size);
this.size = size;
return; return;
} }
fs.ftruncateSync(this.fd, this.size); fs.ftruncateSync(this.fd, size);
this.size = size;
};
BlockData.prototype.truncateAsync = function truncateAsync(size, callback) {
var self = this;
callback = utils.asyncify(callback);
if (!bcoin.fs) {
this.ramdisk.truncate(size);
this.size = size;
return callback();
}
fs.ftruncate(this.fd, size, function(err) {
if (err)
return callback(err);
self.size = size;
return callback();
});
}; };
BlockData.prototype._readSync = function _readSync(size, offset) { BlockData.prototype._readSync = function _readSync(size, offset) {

View File

@ -110,6 +110,11 @@ Chain.prototype._init = function _init() {
utils.debug('Starting chain load at height: %s', i); utils.debug('Starting chain load at height: %s', i);
function doneForReal() {
self.loading = false;
self.emit('load');
}
function done(height) { function done(height) {
if (height != null) { if (height != null) {
utils.debug( utils.debug(
@ -119,8 +124,33 @@ Chain.prototype._init = function _init() {
} else { } else {
utils.debug('Chain successfully loaded.'); utils.debug('Chain successfully loaded.');
} }
self.loading = false;
self.emit('load'); if (!self.blockdb)
return doneForReal();
return doneForReal();
self.blockdb.getHeight(function(err, height) {
if (err)
throw err;
if (height === self.tip.height)
return doneForReal();
utils.debug('ChainDB and BlockDB are out of sync. Syncing...');
if (height < self.tip.height)
return self.resetHeight(height);
if (height > self.tip.height) {
return self.blockdb.resetHeight(self.tip.height, function(err) {
if (err)
throw err;
return doneForReal();
});
}
});
} }
(function next() { (function next() {
@ -267,7 +297,7 @@ Chain.prototype._saveBlock = function _saveBlock(block, callback) {
if (!this.blockdb) if (!this.blockdb)
return callback(); return callback();
this.blockdb.block.saveBlock(block, function(err) { this.blockdb.saveBlock(block, function(err) {
if (err) if (err)
return callback(err); return callback(err);
@ -474,6 +504,7 @@ Chain.prototype._verify = function _verify(block, prev) {
}; };
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) { Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) {
var self = this;
var height = prev.height + 1; var height = prev.height + 1;
var pending = block.txs.length; var pending = block.txs.length;
var called; var called;
@ -521,6 +552,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
}; };
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) { Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
var self = this;
var height = prev.height + 1; var height = prev.height + 1;
if (!this.blockdb || block.subtype !== 'block') if (!this.blockdb || block.subtype !== 'block')
@ -542,7 +574,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
// Check all transactions // Check all transactions
for (i = 0; i < block.txs.length; i++) { for (i = 0; i < block.txs.length; i++) {
tx = blocks.txs[i]; tx = block.txs[i];
hash = tx.hash('hex'); hash = tx.hash('hex');
for (j = 0; j < tx.inputs.length; j++) { for (j = 0; j < tx.inputs.length; j++) {

View File

@ -85,7 +85,34 @@ ChainDB.prototype._init = function _init() {
this.fd = fs.openSync(this.file, 'r+'); this.fd = fs.openSync(this.file, 'r+');
}; };
ChainDB.prototype._malloc = function(size) { ChainDB.prototype.closeSync = function closeSync() {
if (!bcoin.fs) {
this.ramdisk = null;
return;
}
fs.closeSync(this.fd);
this.fd = null;
};
ChainDB.prototype.closeAsync = function closeAsync(callback) {
var self = this;
callback = utils.asyncify(callback);
if (!bcoin.fs) {
this.ramdisk = null;
return callback();
}
fs.close(this.fd, function(err) {
if (err)
return callback(err);
self.fd = null;
return callback();
});
};
ChainDB.prototype._malloc = function _malloc(size) {
if (!this._bufferPool[size]) if (!this._bufferPool[size])
this._bufferPool[size] = new Buffer(size); this._bufferPool[size] = new Buffer(size);
@ -97,7 +124,7 @@ ChainDB.prototype._malloc = function(size) {
return this._bufferPool[size]; return this._bufferPool[size];
}; };
ChainDB.prototype._free = function(buf) { ChainDB.prototype._free = function _free(buf) {
if (this._bufferPool.used[buf.length] === buf) { if (this._bufferPool.used[buf.length] === buf) {
assert(this._bufferPool[buf.length] === buf); assert(this._bufferPool[buf.length] === buf);
delete this._bufferPool.used[buf.length]; delete this._bufferPool.used[buf.length];
@ -312,22 +339,49 @@ ChainDB.prototype.remove = function remove(height) {
assert(height >= 0); assert(height >= 0);
this.truncate(height); this.truncateSync(height);
} }
return true; return true;
}; };
ChainDB.prototype.truncate = function truncate(height) { ChainDB.prototype.truncateSync = function truncateSync(height) {
this.size = (height + 1) * BLOCK_SIZE; var size = (height + 1) * BLOCK_SIZE;
this.tip = height;
if (!bcoin.fs) { if (!bcoin.fs) {
this.ramdisk.truncate(this.size); this.ramdisk.truncate(size);
this.size = size;
this.tip = height;
return; return;
} }
fs.ftruncateSync(this.fd, this.size); fs.ftruncateSync(this.fd, size);
this.size = size;
this.tip = height;
};
ChainDB.prototype.truncateAsync = function truncateAsync(height) {
var self = this;
var size = (height + 1) * BLOCK_SIZE;
callback = utils.asyncify(callback);
if (!bcoin.fs) {
this.ramdisk.truncate(size);
this.size = size;
this.tip = height;
return callback();
}
fs.ftruncate(this.fd, size, function(err) {
if (err)
return callback(err);
self.size = size;
self.tip = height;
return callback();
});
}; };
ChainDB.prototype.isNull = function isNull(height) { ChainDB.prototype.isNull = function isNull(height) {