chaindb: refactor writes and pruning.

This commit is contained in:
Christopher Jeffrey 2016-10-04 22:34:46 -07:00
parent b5dee34f24
commit 1f790f94b9
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -40,7 +40,6 @@ var DUMMY = new Buffer([0]);
* C[addr-hash][hash][index] -> dummy (coin by address)
* W+T[witaddr-hash][hash] -> dummy (tx by address)
* W+C[witaddr-hash][hash][index] -> dummy (coin by address)
* q[height] -> block hash to be pruned
*/
var layout = {
@ -69,9 +68,6 @@ var layout = {
u: function u(hash) {
return pair(0x75, hash);
},
q: function q(height) {
return ipair(0x71, height);
},
T: function T(address, hash) {
var len = address.length;
var key;
@ -228,11 +224,11 @@ ChainDB.prototype._open = co(function* open() {
yield this.db.checkVersion('V', 1);
state = yield this.db.get(layout.R);
state = yield this.getState();
if (state) {
// Grab the chainstate if we have one.
this.state = ChainState.fromRaw(state);
this.state = state;
} else {
// Otherwise write the genesis block.
// (We assume this database is fresh).
@ -482,7 +478,7 @@ ChainDB.prototype.getBoth = co(function* getBoth(block) {
if (hash == null)
height = -1;
return [hash, height];
return new BlockPair(hash, height);
}
height = yield this.getHeight(hash);
@ -490,7 +486,7 @@ ChainDB.prototype.getBoth = co(function* getBoth(block) {
if (height === -1)
hash = null;
return [hash, height];
return new BlockPair(hash, height);
});
/**
@ -554,26 +550,39 @@ ChainDB.prototype.get = co(function* get(hash) {
*/
ChainDB.prototype.save = co(function* save(entry, block, view) {
this.start();
try {
yield this._save(entry, block, view);
} catch (e) {
this.drop();
throw e;
}
yield this.commit();
});
/**
* Save an entry without a batch.
* @private
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView?} view
* @returns {Promise}
*/
ChainDB.prototype._save = co(function* save(entry, block, view) {
var hash = block.hash();
var height = new Buffer(4);
height.writeUInt32LE(entry.height, 0, true);
this.start();
this.put(layout.h(hash), height);
this.put(layout.e(hash), entry.toRaw());
this.cacheHash.set(entry.hash, entry);
if (!view) {
try {
yield this.saveBlock(block);
} catch (e) {
this.drop();
throw e;
}
return yield this.commit();
yield this.saveBlock(block);
return;
}
this.cacheHeight.set(entry.height, entry);
@ -581,16 +590,9 @@ ChainDB.prototype.save = co(function* save(entry, block, view) {
this.put(layout.n(entry.prevBlock), hash);
this.put(layout.H(entry.height), hash);
try {
yield this.saveBlock(block, view);
} catch (e) {
this.drop();
throw e;
}
yield this.saveBlock(block, view);
this.put(layout.R, this.pending.commit(hash));
yield this.commit();
});
/**
@ -602,19 +604,50 @@ ChainDB.prototype.getTip = function getTip() {
return this.get(this.state.hash);
};
/**
* Retrieve the tip entry from the tip record.
* @returns {Promise} - Returns {@link ChainEntry}.
*/
ChainDB.prototype.getState = co(function* getState() {
var data = yield this.db.get(layout.R);
if (!data)
return;
return ChainState.fromRaw(data);
});
/**
* Reconnect the block to the chain.
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView} view
* @returns {Promise} -
* Returns [Error, {@link ChainEntry}, {@link Block}].
* @returns {Promise}
*/
ChainDB.prototype.reconnect = co(function* reconnect(entry, block, view) {
var hash = block.hash();
this.start();
try {
yield this._reconnect(entry, block, view);
} catch (e) {
this.drop();
throw e;
}
yield this.commit();
});
/**
* Reconnect block without a batch.
* @private
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView} view
* @returns {Promise}
*/
ChainDB.prototype._reconnect = co(function* reconnect(entry, block, view) {
var hash = block.hash();
this.put(layout.n(entry.prevBlock), hash);
this.put(layout.H(entry.height), hash);
@ -622,31 +655,46 @@ ChainDB.prototype.reconnect = co(function* reconnect(entry, block, view) {
this.cacheHash.set(entry.hash, entry);
this.cacheHeight.set(entry.height, entry);
try {
yield this.connectBlock(block, view);
} catch (e) {
this.drop();
throw e;
}
yield this.connectBlock(block, view);
this.put(layout.R, this.pending.commit(hash));
yield this.commit();
return block;
});
/**
* Disconnect block from the chain.
* @param {ChainEntry} entry
* @returns {Promise} -
* Returns [Error, {@link ChainEntry}, {@link Block}].
* @returns {Promise}
*/
ChainDB.prototype.disconnect = co(function* disconnect(entry) {
var block;
this.start();
try {
block = yield this._disconnect(entry);
} catch (e) {
this.drop();
throw e;
}
yield this.commit();
return block;
});
/**
* Disconnect block without a batch.
* @private
* @param {ChainEntry} entry
* @returns {Promise}
*/
ChainDB.prototype._disconnect = co(function* disconnect(entry) {
var block;
this.del(layout.n(entry.prevBlock));
this.del(layout.H(entry.height));
@ -654,33 +702,18 @@ ChainDB.prototype.disconnect = co(function* disconnect(entry) {
if (this.options.spv) {
this.put(layout.R, this.pending.commit(entry.prevBlock));
yield this.commit();
return entry.toHeaders();
}
try {
block = yield this.getBlock(entry.hash);
} catch (e) {
this.drop();
throw e;
}
block = yield this.getBlock(entry.hash);
if (!block) {
this.drop();
if (!block)
throw new Error('Block not found.');
}
try {
yield this.disconnectBlock(block);
} catch (e) {
this.drop();
throw e;
}
yield this.disconnectBlock(block);
this.put(layout.R, this.pending.commit(entry.prevBlock));
yield this.commit();
return block;
});
@ -782,14 +815,8 @@ ChainDB.prototype.reset = co(function* reset(block) {
*/
ChainDB.prototype.has = co(function* has(block) {
var items, hash;
checkHash(block);
items = yield this.getBoth(block);
hash = items[0];
return hash != null;
var item = yield this.getBoth(block);
return item.hash != null;
});
/**
@ -802,16 +829,14 @@ ChainDB.prototype.has = co(function* has(block) {
ChainDB.prototype.saveBlock = co(function* saveBlock(block, view) {
if (this.options.spv)
return block;
return;
this.put(layout.b(block.hash()), block.toRaw());
if (!view)
return block;
return;
yield this.connectBlock(block, view);
return block;
});
/**
@ -844,12 +869,12 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
var hashes, address, hash, coins, raw;
if (this.options.spv)
return block;
return;
// Genesis block's coinbase is unspendable.
if (this.chain.isGenesis(block)) {
this.pending.connect(block);
return block;
return;
}
this.pending.connect(block);
@ -927,8 +952,6 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
this.put(layout.u(block.hash()), undo.render());
yield this.pruneBlock(block);
return block;
});
/**
@ -942,7 +965,7 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
var hashes, address, hash, coins, raw;
if (this.options.spv)
return block;
return;
view = yield this.getUndoView(block);
@ -1021,8 +1044,6 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
}
this.del(layout.u(block.hash()));
return block;
});
/**
@ -1464,22 +1485,19 @@ ChainDB.prototype.getUndoView = co(function* getUndoView(block) {
*/
ChainDB.prototype.getBlock = co(function* getBlock(hash) {
var items = yield this.getBoth(hash);
var item = yield this.getBoth(hash);
var height, data, block;
hash = items[0];
height = items[1];
if (!hash)
if (!item.hash)
return;
data = yield this.db.get(layout.b(hash));
data = yield this.db.get(layout.b(item.hash));
if (!data)
return;
block = Block.fromRaw(data);
block.setHeight(height);
block.setHeight(item.height);
return block;
});
@ -1519,7 +1537,7 @@ ChainDB.prototype.hasCoins = function hasCoins(hash) {
*/
ChainDB.prototype.pruneBlock = co(function* pruneBlock(block) {
var futureHeight, key, hash;
var height, hash;
if (this.options.spv)
return;
@ -1527,21 +1545,16 @@ ChainDB.prototype.pruneBlock = co(function* pruneBlock(block) {
if (!this.prune)
return;
if (block.height <= this.network.block.pruneAfterHeight)
height = block.height - this.keepBlocks;
if (height <= this.network.block.pruneAfterHeight)
return;
futureHeight = block.height + this.keepBlocks;
this.put(layout.q(futureHeight), block.hash());
key = layout.q(block.height);
hash = yield this.db.get(key);
hash = yield this.getHash(height);
if (!hash)
return;
this.del(key);
this.del(layout.b(hash));
this.del(layout.u(hash));
});
@ -1654,6 +1667,11 @@ function checkHash(hash) {
'Must pass in height or hash.');
}
function BlockPair(hash, height) {
this.hash = hash;
this.height = height;
}
/*
* Expose
*/