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.crypto = require('cry' + 'pto');
bcoin.net = require('n' + 'et');
bcoin.cp = require('child_' + 'process');
}
bcoin.elliptic = elliptic;

View File

@ -63,6 +63,21 @@ function BlockDB(options) {
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) {
var options, db, pending, stream, total, done;
@ -251,14 +266,23 @@ BlockDB.prototype.removeBlock = function removeBlock(hash, callback) {
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);
function done() {
batch.write(function(err) {
if (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) {
var id = 'u/t/' + hash + '/' + index;
@ -817,7 +864,15 @@ BlockDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
if (err && err.type !== 'NotFoundError')
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')
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
*/
@ -888,7 +1002,34 @@ BlockData.prototype._init = function _init() {
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)
return new Buffer(size);
@ -903,7 +1044,7 @@ BlockData.prototype._malloc = function(size) {
return this._bufferPool[size];
};
BlockData.prototype._free = function(buf) {
BlockData.prototype._free = function _free(buf) {
if (this._bufferPool.used[buf.length] === buf) {
assert(this._bufferPool[buf.length] === buf);
delete this._bufferPool.used[buf.length];
@ -967,15 +1108,34 @@ BlockData.prototype.saveAsync = function saveAsync(data, callback) {
});
};
BlockData.prototype.truncate = function truncate(size) {
this.size = size;
BlockData.prototype.truncateSync = function truncateSync(size) {
if (!bcoin.fs) {
this.ramdisk.truncate(this.size);
this.ramdisk.truncate(size);
this.size = size;
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) {

View File

@ -110,6 +110,11 @@ Chain.prototype._init = function _init() {
utils.debug('Starting chain load at height: %s', i);
function doneForReal() {
self.loading = false;
self.emit('load');
}
function done(height) {
if (height != null) {
utils.debug(
@ -119,8 +124,33 @@ Chain.prototype._init = function _init() {
} else {
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() {
@ -267,7 +297,7 @@ Chain.prototype._saveBlock = function _saveBlock(block, callback) {
if (!this.blockdb)
return callback();
this.blockdb.block.saveBlock(block, function(err) {
this.blockdb.saveBlock(block, function(err) {
if (err)
return callback(err);
@ -474,6 +504,7 @@ Chain.prototype._verify = function _verify(block, prev) {
};
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) {
var self = this;
var height = prev.height + 1;
var pending = block.txs.length;
var called;
@ -521,6 +552,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
};
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
var self = this;
var height = prev.height + 1;
if (!this.blockdb || block.subtype !== 'block')
@ -542,7 +574,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
// Check all transactions
for (i = 0; i < block.txs.length; i++) {
tx = blocks.txs[i];
tx = block.txs[i];
hash = tx.hash('hex');
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+');
};
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])
this._bufferPool[size] = new Buffer(size);
@ -97,7 +124,7 @@ ChainDB.prototype._malloc = function(size) {
return this._bufferPool[size];
};
ChainDB.prototype._free = function(buf) {
ChainDB.prototype._free = function _free(buf) {
if (this._bufferPool.used[buf.length] === buf) {
assert(this._bufferPool[buf.length] === buf);
delete this._bufferPool.used[buf.length];
@ -312,22 +339,49 @@ ChainDB.prototype.remove = function remove(height) {
assert(height >= 0);
this.truncate(height);
this.truncateSync(height);
}
return true;
};
ChainDB.prototype.truncate = function truncate(height) {
this.size = (height + 1) * BLOCK_SIZE;
this.tip = height;
ChainDB.prototype.truncateSync = function truncateSync(height) {
var size = (height + 1) * BLOCK_SIZE;
if (!bcoin.fs) {
this.ramdisk.truncate(this.size);
this.ramdisk.truncate(size);
this.size = size;
this.tip = height;
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) {