chaindb: index all tips.

This commit is contained in:
Christopher Jeffrey 2016-11-10 18:13:31 -08:00
parent 42cc8c4698
commit ee19c7fee6
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 75 additions and 16 deletions

View File

@ -24,6 +24,9 @@ var layout = {
n: function n(hash) {
return 'n' + hex(hash);
},
a: function a(hash) {
return 'a' + hex(hash);
},
b: function b(hash) {
return 'b' + hex(hash);
},
@ -52,6 +55,9 @@ var layout = {
return 'C' + address + hex(hash) + pad32(index);
},
aa: function aa(key) {
return key.slice(1, 65);
},
Cc: function Cc(key) {
var hash, index;

View File

@ -36,6 +36,7 @@ var DUMMY = new Buffer([0]);
* h[hash] -> height
* H[height] -> hash
* n[hash] -> next hash
* a[hash] -> tip index
* b[hash] -> block
* t[hash] -> extended tx
* c[hash] -> coins
@ -61,6 +62,9 @@ var layout = {
n: function n(hash) {
return pair(0x6e, hash);
},
a: function a(hash) {
return pair(0x61, hash);
},
b: function b(hash) {
return pair(0x62, hash);
},
@ -117,6 +121,9 @@ var layout = {
return key;
},
aa: function aa(key) {
return key.toString('hex', 1, 33);
},
Cc: function Cc(key) {
var hash, index;
@ -660,6 +667,19 @@ ChainDB.prototype.getEntries = function getEntries() {
});
};
/**
* Get all tip hashes.
* @returns {Promise} - Returns {@link Hash}[].
*/
ChainDB.prototype.getTips = function getTips() {
return this.db.keys({
gte: layout.a(constants.ZERO_HASH),
lte: layout.a(constants.MAX_HASH),
parse: layout.aa
});
};
/**
* Get a coin (unspents only).
* @param {Hash} hash
@ -1209,23 +1229,34 @@ ChainDB.prototype.save = co(function* save(entry, block, view) {
ChainDB.prototype._save = co(function* save(entry, block, view) {
var hash = block.hash();
// Hash->height index
this.put(layout.h(hash), U32(entry.height));
this.put(layout.e(hash), entry.toRaw());
// Entry data
this.put(layout.e(hash), entry.toRaw());
this.cacheHash.push(entry.hash, entry);
// Tip index
this.del(layout.a(entry.prevBlock));
this.put(layout.a(hash), DUMMY);
if (!view) {
// Save block data
yield this.saveBlock(block);
return;
}
// Hash->next-block index
this.put(layout.n(entry.prevBlock), hash);
// Height->hash index
this.put(layout.H(entry.height), hash);
this.cacheHeight.push(entry.height, entry);
this.put(layout.n(entry.prevBlock), hash);
this.put(layout.H(entry.height), hash);
// Connect block and save data
yield this.saveBlock(block, view);
// New chain state
this.put(layout.R, this.pending.commit(hash));
});
@ -1341,6 +1372,12 @@ ChainDB.prototype.reset = co(function* reset(block) {
if (this.options.prune)
throw new Error('Cannot reset when pruned.');
// We need to remove all alternate
// chains first. This is ugly, but
// it's the only safe way to reset
// the chain.
yield this.removeChains();
tip = yield this.getTip();
assert(tip);
@ -1357,6 +1394,9 @@ ChainDB.prototype.reset = co(function* reset(block) {
assert(!tip.isGenesis());
this.del(layout.a(tip.hash));
this.put(layout.a(tip.prevBlock), DUMMY);
this.del(layout.H(tip.height));
this.del(layout.h(tip.hash));
this.del(layout.e(tip.hash));
@ -1384,25 +1424,39 @@ ChainDB.prototype.reset = co(function* reset(block) {
});
/**
* Remove an alternate chain.
* @param {ChainEntry} tip
* @param {ChainEntry} entry
* Remove all alternate chains.
* @returns {Promise}
*/
ChainDB.prototype.removeChain = co(function* removeChain(tip, entry) {
this.logger.debug('Removing alternate chain: %s->%s', tip.rhash, entry.rhash);
ChainDB.prototype.removeChains = co(function* removeChains() {
var tips = yield this.getTips();
var i;
for (i = 0; i < tips.length; i++)
yield this.removeChain(tips[i]);
});
/**
* Remove an alternate chain.
* @param {Hash} hash - Alternate chain tip.
* @returns {Promise}
*/
ChainDB.prototype.removeChain = co(function* removeChain(hash) {
var tip = yield this.get(hash);
if (!tip)
throw new Error('Alternate chain tip not found.');
this.logger.debug('Removing alternate chain: %s.', tip.rhash);
this.start();
for (;;) }
if (tip.hash === entry.hash)
while (tip) {
if (yield tip.isMainChain())
break;
if (tip.isGenesis()) {
this.drop();
throw new Error('Target entry not in chain.');
}
assert(!tip.isGenesis());
this.del(layout.h(tip.hash));
this.del(layout.e(tip.hash));
@ -1411,7 +1465,6 @@ ChainDB.prototype.removeChain = co(function* removeChain(tip, entry) {
this.cacheHash.unpush(tip.hash);
tip = yield this.get(tip.prevBlock);
assert(tip);
}
yield this.commit();