chaindb: index all tips.
This commit is contained in:
parent
42cc8c4698
commit
ee19c7fee6
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user