chaindb: index all tips.
This commit is contained in:
parent
42cc8c4698
commit
ee19c7fee6
@ -24,6 +24,9 @@ var layout = {
|
|||||||
n: function n(hash) {
|
n: function n(hash) {
|
||||||
return 'n' + hex(hash);
|
return 'n' + hex(hash);
|
||||||
},
|
},
|
||||||
|
a: function a(hash) {
|
||||||
|
return 'a' + hex(hash);
|
||||||
|
},
|
||||||
b: function b(hash) {
|
b: function b(hash) {
|
||||||
return 'b' + hex(hash);
|
return 'b' + hex(hash);
|
||||||
},
|
},
|
||||||
@ -52,6 +55,9 @@ var layout = {
|
|||||||
|
|
||||||
return 'C' + address + hex(hash) + pad32(index);
|
return 'C' + address + hex(hash) + pad32(index);
|
||||||
},
|
},
|
||||||
|
aa: function aa(key) {
|
||||||
|
return key.slice(1, 65);
|
||||||
|
},
|
||||||
Cc: function Cc(key) {
|
Cc: function Cc(key) {
|
||||||
var hash, index;
|
var hash, index;
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@ var DUMMY = new Buffer([0]);
|
|||||||
* h[hash] -> height
|
* h[hash] -> height
|
||||||
* H[height] -> hash
|
* H[height] -> hash
|
||||||
* n[hash] -> next hash
|
* n[hash] -> next hash
|
||||||
|
* a[hash] -> tip index
|
||||||
* b[hash] -> block
|
* b[hash] -> block
|
||||||
* t[hash] -> extended tx
|
* t[hash] -> extended tx
|
||||||
* c[hash] -> coins
|
* c[hash] -> coins
|
||||||
@ -61,6 +62,9 @@ var layout = {
|
|||||||
n: function n(hash) {
|
n: function n(hash) {
|
||||||
return pair(0x6e, hash);
|
return pair(0x6e, hash);
|
||||||
},
|
},
|
||||||
|
a: function a(hash) {
|
||||||
|
return pair(0x61, hash);
|
||||||
|
},
|
||||||
b: function b(hash) {
|
b: function b(hash) {
|
||||||
return pair(0x62, hash);
|
return pair(0x62, hash);
|
||||||
},
|
},
|
||||||
@ -117,6 +121,9 @@ var layout = {
|
|||||||
|
|
||||||
return key;
|
return key;
|
||||||
},
|
},
|
||||||
|
aa: function aa(key) {
|
||||||
|
return key.toString('hex', 1, 33);
|
||||||
|
},
|
||||||
Cc: function Cc(key) {
|
Cc: function Cc(key) {
|
||||||
var hash, index;
|
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).
|
* Get a coin (unspents only).
|
||||||
* @param {Hash} hash
|
* @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) {
|
ChainDB.prototype._save = co(function* save(entry, block, view) {
|
||||||
var hash = block.hash();
|
var hash = block.hash();
|
||||||
|
|
||||||
|
// Hash->height index
|
||||||
this.put(layout.h(hash), U32(entry.height));
|
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);
|
this.cacheHash.push(entry.hash, entry);
|
||||||
|
|
||||||
|
// Tip index
|
||||||
|
this.del(layout.a(entry.prevBlock));
|
||||||
|
this.put(layout.a(hash), DUMMY);
|
||||||
|
|
||||||
if (!view) {
|
if (!view) {
|
||||||
|
// Save block data
|
||||||
yield this.saveBlock(block);
|
yield this.saveBlock(block);
|
||||||
return;
|
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.cacheHeight.push(entry.height, entry);
|
||||||
|
|
||||||
this.put(layout.n(entry.prevBlock), hash);
|
// Connect block and save data
|
||||||
this.put(layout.H(entry.height), hash);
|
|
||||||
|
|
||||||
yield this.saveBlock(block, view);
|
yield this.saveBlock(block, view);
|
||||||
|
|
||||||
|
// New chain state
|
||||||
this.put(layout.R, this.pending.commit(hash));
|
this.put(layout.R, this.pending.commit(hash));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1341,6 +1372,12 @@ ChainDB.prototype.reset = co(function* reset(block) {
|
|||||||
if (this.options.prune)
|
if (this.options.prune)
|
||||||
throw new Error('Cannot reset when pruned.');
|
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();
|
tip = yield this.getTip();
|
||||||
assert(tip);
|
assert(tip);
|
||||||
|
|
||||||
@ -1357,6 +1394,9 @@ ChainDB.prototype.reset = co(function* reset(block) {
|
|||||||
|
|
||||||
assert(!tip.isGenesis());
|
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.height));
|
||||||
this.del(layout.h(tip.hash));
|
this.del(layout.h(tip.hash));
|
||||||
this.del(layout.e(tip.hash));
|
this.del(layout.e(tip.hash));
|
||||||
@ -1384,25 +1424,39 @@ ChainDB.prototype.reset = co(function* reset(block) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an alternate chain.
|
* Remove all alternate chains.
|
||||||
* @param {ChainEntry} tip
|
|
||||||
* @param {ChainEntry} entry
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ChainDB.prototype.removeChain = co(function* removeChain(tip, entry) {
|
ChainDB.prototype.removeChains = co(function* removeChains() {
|
||||||
this.logger.debug('Removing alternate chain: %s->%s', tip.rhash, entry.rhash);
|
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();
|
this.start();
|
||||||
|
|
||||||
for (;;) }
|
while (tip) {
|
||||||
if (tip.hash === entry.hash)
|
if (yield tip.isMainChain())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (tip.isGenesis()) {
|
assert(!tip.isGenesis());
|
||||||
this.drop();
|
|
||||||
throw new Error('Target entry not in chain.');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.del(layout.h(tip.hash));
|
this.del(layout.h(tip.hash));
|
||||||
this.del(layout.e(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);
|
this.cacheHash.unpush(tip.hash);
|
||||||
|
|
||||||
tip = yield this.get(tip.prevBlock);
|
tip = yield this.get(tip.prevBlock);
|
||||||
assert(tip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
yield this.commit();
|
yield this.commit();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user