diff --git a/backend/src/api/chain-tips.ts b/backend/src/api/chain-tips.ts index 096bda142..0e064edb3 100644 --- a/backend/src/api/chain-tips.ts +++ b/backend/src/api/chain-tips.ts @@ -24,6 +24,7 @@ export interface StaleTip extends ChainTip { export interface OrphanedBlock { height: number; hash: string; + branchlen: number; status: 'valid-fork' | 'valid-headers' | 'headers-only'; prevhash: string; } @@ -70,6 +71,7 @@ class ChainTips { orphan = { height: block.height, hash: block.id, + branchlen: chain.branchlen - 1, status: chain.status, prevhash: block.previousblockhash, }; @@ -173,7 +175,7 @@ class ChainTips { this.staleTips[staleBlock.height] = { height: staleBlock.height, hash: staleBlock.id, - branchlen: tip.height - staleBlock.height, + branchlen: tip.branchlen, status: tip.status, stale: staleBlock, canonical: canonicalBlock, @@ -213,7 +215,7 @@ class ChainTips { return Object.values(this.staleTips).sort((a, b) => b.height - a.height); } - /** @asyncSafe */ + /** @asyncUnsafe */ public async $getStaleTipsPage(fromHeight: number | undefined, limit: number): Promise { const cacheTips = this.getStaleTips(); if (!Common.indexingEnabled()) { @@ -221,15 +223,7 @@ class ChainTips { return page.slice(0, limit); } - const staleBlocks = await BlocksRepository.$getStaleBlocks(fromHeight, limit); - - // map the previous block hashes of each stale block so we can reconstruct branch lengths - const staleByPrevhash: { [prevhash: string]: BlockExtended } = {}; - for (const block of staleBlocks) { - if (block.previousblockhash) { - staleByPrevhash[block.previousblockhash] = block; - } - } + const staleTips = await BlocksRepository.$getStaleTips(fromHeight, limit); const cacheTipsMap = new Map(); for (const cacheTip of cacheTips) { @@ -237,29 +231,31 @@ class ChainTips { } const tips: StaleTip[] = []; - for (const staleBlock of staleBlocks) { + for (const staleTip of staleTips) { - const cachedTip = cacheTipsMap.get(staleBlock.id); + const cachedTip = cacheTipsMap.get(staleTip.id); if (cachedTip) { tips.push(cachedTip); continue; } - const canonical = await BlocksRepository.$getBlockByHeight(staleBlock.height); + const canonical = await BlocksRepository.$getBlockByHeight(staleTip.height); if (!canonical) { continue; } - // walk up the stale branch to find its tip, depth = branchTipHeight - staleHeight - let branchTip = staleBlock; - while (staleByPrevhash[branchTip.id]) { - branchTip = staleByPrevhash[branchTip.id]; + + let prevBlock = await BlocksRepository.$getBlockByHash(staleTip.previousblockhash); + let branchlen = 0; + while(prevBlock?.stale) { + prevBlock = await BlocksRepository.$getBlockByHash(prevBlock.previousblockhash); + branchlen++; } tips.push({ - height: staleBlock.height, - hash: staleBlock.id, - branchlen: branchTip.height - staleBlock.height, - stale: staleBlock, + height: staleTip.height, + hash: staleTip.id, + branchlen, + stale: staleTip, canonical, }); } diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 571dd2521..a40e7589f 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -604,7 +604,7 @@ class BlocksRepository { * When fromHeight is defined, only stale blocks strictly below it are returned. * @asyncSafe */ - public async $getStaleBlocks(fromHeight: number | undefined, limit: number): Promise { + public async $getStaleTips(fromHeight: number | undefined, limit: number): Promise { try { const params: (number)[] = []; let heightFilter = ''; @@ -619,18 +619,23 @@ class BlocksRepository { FROM blocks JOIN pools ON blocks.pool_id = pools.id WHERE blocks.stale = 1${heightFilter} + AND NOT EXISTS ( + SELECT 1 FROM blocks AS child + WHERE child.previous_block_hash = blocks.hash + AND child.stale = 1 + ) ORDER BY blocks.height DESC LIMIT ?`, params ); - const staleBlocks: BlockExtended[] = []; + const staleTips: BlockExtended[] = []; for (const row of rows) { - staleBlocks.push(await this.formatDbBlockIntoExtendedBlock(row as DatabaseBlock)); + staleTips.push(await this.formatDbBlockIntoExtendedBlock(row as DatabaseBlock)); } - return staleBlocks; + return staleTips; } catch (e) { - logger.err(`Cannot get stale blocks. Reason: ` + (e instanceof Error ? e.message : e)); + logger.err(`Cannot get stale tips. Reason: ` + (e instanceof Error ? e.message : e)); throw e; } } @@ -1292,6 +1297,7 @@ class BlocksRepository { blk.previousblockhash = dbBlk.previousblockhash; blk.mediantime = dbBlk.mediantime; blk.indexVersion = dbBlk.index_version; + blk.stale = dbBlk.stale; // BlockExtension extras.totalFees = dbBlk.totalFees; extras.medianFee = dbBlk.medianFee;