Skip to content

Commit

Permalink
chain: fix tree interval reorg bug. see #248.
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Sep 11, 2019
1 parent cc35dc2 commit 17db508
Show file tree
Hide file tree
Showing 3 changed files with 672 additions and 17 deletions.
2 changes: 1 addition & 1 deletion lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class Chain extends AsyncEmitter {

assert((height % this.network.names.treeInterval) !== 0);

await this.db.saveNames(view, height);
await this.db.saveNames(view, entry, false);
}

this.logger.info('Synchronized Tree Root: %x.', this.db.txn.rootHash());
Expand Down
53 changes: 37 additions & 16 deletions lib/blockchain/chaindb.js
Original file line number Diff line number Diff line change
Expand Up @@ -1824,10 +1824,11 @@ class ChainDB {
* Commit names to tree.
* @private
* @param {CoinView} view
* @param {Number} height
* @param {ChainEntry} entry
* @param {Boolean} revert
*/

async saveNames(view, height) {
async saveNames(view, entry, revert) {
for (const ns of view.names.values()) {
const {nameHash} = ns;

Expand All @@ -1839,37 +1840,57 @@ class ChainDB {
await this.txn.insert(nameHash, ns.encode());
}

if ((height % this.network.names.treeInterval) === 0)
this.put(layout.s.encode(), await this.txn.commit());
if ((entry.height % this.network.names.treeInterval) === 0) {
// Explanation:
//
// During a reorg, we must revert the snapshot
// back to the beginning of the interval. We
// can still incrementally revert the database
// transaction with state deltas, but unless
// we get the tree hash back to what it was
// at the start, we will end up rejecting blocks
// during reconnection.
//
// This is an invalid state that cannot be
// recovered from. Luckily, the block we're
// disconnecting commits to the _previous_ tree
// root, not the current one.
if (revert)
await this.tree.inject(entry.treeRoot);
else
await this.txn.commit();

this.put(layout.s.encode(), this.tree.rootHash());
}
}

/**
* Connect names to tree.
* @private
* @param {CoinView} view
* @param {Number} height
* @param {ChainEntry} entry
*/

async connectNames(view, height) {
async connectNames(view, entry) {
const undo = view.toNameUndo();

if (undo.names.length === 0)
this.del(layout.w.encode(height));
this.del(layout.w.encode(entry.height));
else
this.put(layout.w.encode(height), undo.encode());
this.put(layout.w.encode(entry.height), undo.encode());

return this.saveNames(view, height);
return this.saveNames(view, entry, false);
}

/**
* Disconnect names from tree.
* @private
* @param {CoinView} view
* @param {Number} height
* @param {ChainEntry} entry
*/

async disconnectNames(view, height) {
const raw = await this.db.get(layout.w.encode(height));
async disconnectNames(view, entry) {
const raw = await this.db.get(layout.w.encode(entry.height));

if (raw) {
const undo = NameUndo.decode(raw);
Expand All @@ -1880,10 +1901,10 @@ class ChainDB {
ns.applyState(delta);
}

this.del(layout.w.encode(height));
this.del(layout.w.encode(entry.height));
}

return this.saveNames(view, height - 1);
return this.saveNames(view, entry, true);
}

/**
Expand Down Expand Up @@ -1946,7 +1967,7 @@ class ChainDB {
await this.pruneBlock(entry);

// Connect name state.
return this.connectNames(view, entry.height);
return this.connectNames(view, entry);
}

/**
Expand Down Expand Up @@ -2018,7 +2039,7 @@ class ChainDB {
this.del(layout.u.encode(hash));

// Connect name state.
await this.disconnectNames(view, entry.height);
await this.disconnectNames(view, entry);

return view;
}
Expand Down
Loading

0 comments on commit 17db508

Please sign in to comment.