Skip to content

Commit

Permalink
chain: implement urkel tree compaction to historical root
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Dec 9, 2021
1 parent 0cd895b commit 33f1975
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
46 changes: 46 additions & 0 deletions lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,52 @@ class Chain extends AsyncEmitter {
}
}

/**
* Compact the Urkel Tree.
* Removes all historical state and all data not
* linked directly to the provided root node hash.
* @param {Hash} root
* @returns {Promise}
*/

async compactTree() {
const unlock = await this.locker.lock();
this.logger.info('Compacting Urkel Tree...');

// To support chain reorgs of limited depth we compact the tree
// to some commitment point in recent history, then rebuild it from there
// back up to the current chain tip. In order to support pruning nodes,
// all blocks above this depth must be available on disk.
// This actually further reduces the ability for a pruning node to recover
// from a deep reorg. On mainnet, `keepBlocks` is 288. A normal pruning
// node can recover from a reorg up to that depth. Compacting the tree
// potentially reduces that depth to 288 - 36 = 252. A reorg deeper than
// that will result in a `MissingNodeError` thrown by Urkel inside
// chain.saveNames() as it tries to restore a deleted state.

// Oldest block available to a pruning node.
const oldestBlock = this.height - this.network.block.keepBlocks;

// Distance from that block to the start of the oldest tree interval.
const toNextInterval =
this.network.names.treeInterval -
(oldestBlock % this.network.names.treeInterval);

// Get the oldest Urkel Tree root state a pruning node can recover from.
const oldestTreeIntervalStart = oldestBlock + toNextInterval + 1;
const entry = await this.db.getEntryByHeight(oldestTreeIntervalStart);

try {
// Rewind Urkel Tree and delete all historical state.
await this.db.compactTree(entry.treeRoot);

// Replay the blockchain back up to the tip to rebuild tree.
return await this.syncTree();
} finally {
unlock();
}
}

/**
* Scan the blockchain for transactions containing specified address hashes.
* @param {Hash} start - Block hash to start at.
Expand Down
19 changes: 19 additions & 0 deletions lib/blockchain/chaindb.js
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,25 @@ class ChainDB {
return true;
}

/**
* Compact the Urkel Tree.
* Removes all historical state and all data not
* linked directly to the provided root node hash.
* @param {Hash} root
* @returns {Promise}
*/

async compactTree(root) {
// Rewind tree to historical commitment
await this.tree.inject(root);

// Delete historical data
await this.tree.compact();

// Reset in-memory tree delta
this.txn = this.tree.txn();
}

/**
* Get the _next_ block hash (does not work by height).
* @param {Hash} hash
Expand Down

0 comments on commit 33f1975

Please sign in to comment.