Skip to content

Commit

Permalink
chain: do not save tree root in levelDB, recover tree state from tree
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Jan 7, 2022
1 parent f691c64 commit 0ddd228
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 11 deletions.
10 changes: 0 additions & 10 deletions lib/blockchain/chaindb.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,6 @@ class ChainDB {
// Grab the chainstate if we have one.
this.state = state;

// Grab the current tree state.
if (!this.options.spv) {
const root = await this.db.get(layout.s.encode());
assert(root && root.length === 32);

await this.tree.inject(root);
}

// Read bitfield.
this.field = await this.getField();

Expand Down Expand Up @@ -1985,8 +1977,6 @@ class ChainDB {
await this.tree.inject(entry.treeRoot);
else
await this.txn.commit();

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

Expand Down
2 changes: 1 addition & 1 deletion lib/blockchain/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const bdb = require('bdb');
* T[addr-hash][hash] -> dummy (tx by address)
* C[addr-hash][hash][index] -> dummy (coin by address)
* w[height] -> name undo
* s -> tree state
* s -> tree state (deprecated)
* f -> bit field
* M -> migration state
*/
Expand Down
47 changes: 47 additions & 0 deletions test/chain-tree-compaction-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,53 @@ describe('Tree Compacting', function() {
assert.bufferEqual(ns.data, Buffer.from([counter - 1]));
});

it('should recover if aborted', async () => {
// Get current counter value.
let raw = await chain.db.tree.get(nameHash);
let ns = NameState.decode(raw);
const counter = ns.data[0];

// Add 20 tree intervals
for (let i = counter; i <= counter + 20; i++) {
send(await wallet.sendUpdate(name, Buffer.from([i])), mempool);
await mineBlocks(treeInterval, mempool);
}

const before = await fs.stat(treePath);

// Rewind the tree 6 intervals and compact, but do not sync to tip yet.
const entry = await chain.getEntry(chain.height - 6 * treeInterval);
await chain.db.compactTree(entry.treeRoot);

// Confirm tree state has been rewound
assert.notBufferEqual(chain.db.tree.rootHash(), chain.tip.treeRoot);

// Oops, we abort before calling chain.syncTree()
await miner.close();
await chain.close();
await blocks.close();

// Restart -- chainDB used to open tree with what it thought
// was the latest tree state (saved in levelDB). If the actual
// tree on disk was still 6 intervals behind, chain.open() would
// fail with `Missing node` error. The updated logic relies on the
// tree itself to find its own state (saved in Meta nodes) then
// chain.syncTree() will catch it up from there to tip.
await blocks.open();
await chain.open();
await miner.open();

// Tree was compacted
const after = await fs.stat(treePath);
assert(before.size > after.size);

// Tree was re-synced automatically to chain tip on restart
assert.bufferEqual(chain.db.tree.rootHash(), chain.tip.treeRoot);
raw = await chain.db.tree.get(nameHash);
ns = NameState.decode(raw);
assert.bufferEqual(ns.data, Buffer.from([counter + 20]));
});

it(`should ${prune ? '' : 'not '}have pruned chain`, async () => {
// Sanity check. Everything worked on a chain that is indeed pruning.
// Start at height 2 because pruneAfterHeight == 1
Expand Down

0 comments on commit 0ddd228

Please sign in to comment.