Merge pull request #3932 from mempool/mononaut/stale-blocks

Stale blocks
This commit is contained in:
softsimon 2023-07-11 14:47:44 +09:00 committed by GitHub
commit 168cc9c1bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 56 additions and 8 deletions

View File

@ -29,6 +29,7 @@ class BitcoinApi implements AbstractBitcoinApi {
weight: block.weight, weight: block.weight,
previousblockhash: block.previousblockhash, previousblockhash: block.previousblockhash,
mediantime: block.mediantime, mediantime: block.mediantime,
stale: block.confirmations === -1,
}; };
} }

View File

@ -89,6 +89,7 @@ export namespace IEsploraApi {
weight: number; weight: number;
previousblockhash: string; previousblockhash: string;
mediantime: number; mediantime: number;
stale: boolean;
} }
export interface Address { export interface Address {

View File

@ -656,10 +656,6 @@ class Blocks {
const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions); const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions);
this.updateTimerProgress(timer, `got block data for ${this.currentBlockHeight}`); this.updateTimerProgress(timer, `got block data for ${this.currentBlockHeight}`);
// start async callbacks
this.updateTimerProgress(timer, `starting async callbacks for ${this.currentBlockHeight}`);
const callbackPromises = this.newAsyncBlockCallbacks.map((cb) => cb(blockExtended, txIds, transactions));
if (Common.indexingEnabled()) { if (Common.indexingEnabled()) {
if (!fastForwarded) { if (!fastForwarded) {
const lastBlock = await blocksRepository.$getBlockByHeight(blockExtended.height - 1); const lastBlock = await blocksRepository.$getBlockByHeight(blockExtended.height - 1);
@ -671,9 +667,11 @@ class Blocks {
await BlocksRepository.$deleteBlocksFrom(lastBlock.height - 10); await BlocksRepository.$deleteBlocksFrom(lastBlock.height - 10);
await HashratesRepository.$deleteLastEntries(); await HashratesRepository.$deleteLastEntries();
await cpfpRepository.$deleteClustersFrom(lastBlock.height - 10); await cpfpRepository.$deleteClustersFrom(lastBlock.height - 10);
this.blocks = this.blocks.slice(0, -10);
this.updateTimerProgress(timer, `rolled back chain divergence from ${this.currentBlockHeight}`); this.updateTimerProgress(timer, `rolled back chain divergence from ${this.currentBlockHeight}`);
for (let i = 10; i >= 0; --i) { for (let i = 10; i >= 0; --i) {
const newBlock = await this.$indexBlock(lastBlock.height - i); const newBlock = await this.$indexBlock(lastBlock.height - i);
this.blocks.push(newBlock);
this.updateTimerProgress(timer, `reindexed block`); this.updateTimerProgress(timer, `reindexed block`);
let cpfpSummary; let cpfpSummary;
if (config.MEMPOOL.CPFP_INDEXING) { if (config.MEMPOOL.CPFP_INDEXING) {
@ -722,6 +720,10 @@ class Blocks {
} }
} }
// start async callbacks
this.updateTimerProgress(timer, `starting async callbacks for ${this.currentBlockHeight}`);
const callbackPromises = this.newAsyncBlockCallbacks.map((cb) => cb(blockExtended, txIds, transactions));
if (block.height % 2016 === 0) { if (block.height % 2016 === 0) {
if (Common.indexingEnabled()) { if (Common.indexingEnabled()) {
await DifficultyAdjustmentsRepository.$saveAdjustments({ await DifficultyAdjustmentsRepository.$saveAdjustments({
@ -814,6 +816,16 @@ class Blocks {
return blockExtended; return blockExtended;
} }
public async $indexStaleBlock(hash: string): Promise<BlockExtended> {
const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(hash);
const transactions = await this.$getTransactionsExtended(hash, block.height, true);
const blockExtended = await this.$getBlockExtended(block, transactions);
blockExtended.canonical = await bitcoinApi.$getBlockHash(block.height);
return blockExtended;
}
/** /**
* Get one block by its hash * Get one block by its hash
*/ */
@ -831,7 +843,11 @@ class Blocks {
// Bitcoin network, add our custom data on top // Bitcoin network, add our custom data on top
const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(hash); const block: IEsploraApi.Block = await bitcoinCoreApi.$getBlock(hash);
return await this.$indexBlock(block.height); if (block.stale) {
return await this.$indexStaleBlock(hash);
} else {
return await this.$indexBlock(block.height);
}
} }
public async $getStrippedBlockTransactions(hash: string, skipMemoryCache = false, public async $getStrippedBlockTransactions(hash: string, skipMemoryCache = false,

View File

@ -227,6 +227,7 @@ export interface BlockExtension {
*/ */
export interface BlockExtended extends IEsploraApi.Block { export interface BlockExtended extends IEsploraApi.Block {
extras: BlockExtension; extras: BlockExtension;
canonical?: string;
} }
export interface BlockSummary { export interface BlockSummary {

View File

@ -62,8 +62,7 @@ class BlocksAuditRepositories {
public async $getBlockAudit(hash: string): Promise<any> { public async $getBlockAudit(hash: string): Promise<any> {
try { try {
const [rows]: any[] = await DB.query( const [rows]: any[] = await DB.query(
`SELECT blocks.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size, `SELECT blocks_audits.height, blocks_audits.hash as id, UNIX_TIMESTAMP(blocks_audits.time) as timestamp,
blocks.weight, blocks.tx_count,
template, template,
missing_txs as missingTxs, missing_txs as missingTxs,
added_txs as addedTxs, added_txs as addedTxs,
@ -73,7 +72,6 @@ class BlocksAuditRepositories {
expected_fees as expectedFees, expected_fees as expectedFees,
expected_weight as expectedWeight expected_weight as expectedWeight
FROM blocks_audits FROM blocks_audits
JOIN blocks ON blocks.hash = blocks_audits.hash
JOIN blocks_templates ON blocks_templates.id = blocks_audits.hash JOIN blocks_templates ON blocks_templates.id = blocks_audits.hash
WHERE blocks_audits.hash = "${hash}" WHERE blocks_audits.hash = "${hash}"
`); `);

View File

@ -1,6 +1,10 @@
<div class="container-xl" (window:resize)="onResize($event)"> <div class="container-xl" (window:resize)="onResize($event)">
<div class="title-block" [class.time-ltr]="timeLtr" id="block"> <div class="title-block" [class.time-ltr]="timeLtr" id="block">
<div *ngIf="block?.stale" class="alert alert-mempool" role="alert">
<span i18n="block.reorged|Block reorg" class="alert-text">This block does not belong to the main chain, it has been replaced by:</span>
<app-truncate [text]="block.canonical" [lastChars]="12" [link]="['/block/' | relativeUrl, block.canonical]" [maxWidth]="480"></app-truncate>
</div>
<h1> <h1>
<ng-container *ngIf="blockHeight == null || blockHeight > 0; else genesis" i18n="shared.block-title">Block</ng-container> <ng-container *ngIf="blockHeight == null || blockHeight > 0; else genesis" i18n="shared.block-title">Block</ng-container>
<ng-template #genesis i18n="@@2303359202781425764">Genesis</ng-template> <ng-template #genesis i18n="@@2303359202781425764">Genesis</ng-template>
@ -23,6 +27,8 @@
<div class="grow"></div> <div class="grow"></div>
<button *ngIf="block?.stale" type="button" class="btn btn-sm btn-danger container-button" i18n="block.stale|Stale block state">Stale</button>
<button [routerLink]="['/' | relativeUrl]" class="btn btn-sm">&#10005;</button> <button [routerLink]="['/' | relativeUrl]" class="btn btn-sm">&#10005;</button>
</div> </div>

View File

@ -1,3 +1,26 @@
.title-block {
flex-wrap: wrap;
align-items: baseline;
@media (min-width: 650px) {
flex-direction: row;
}
h1 {
margin: 0rem;
margin-right: 15px;
line-height: 1;
}
.alert-mempool {
flex-direction: row;
flex-wrap: wrap;
}
.container-button {
align-self: center;
margin-right: 1em;
}
}
.qr-wrapper { .qr-wrapper {
background-color: #FFF; background-color: #FFF;
padding: 10px; padding: 10px;

View File

@ -120,6 +120,8 @@ export interface Block {
size: number; size: number;
weight: number; weight: number;
previousblockhash: string; previousblockhash: string;
stale?: boolean;
canonical?: string;
} }
export interface Address { export interface Address {