Separate summary and audit-summary API endpoints
This commit is contained in:
		
							parent
							
								
									e5efc2957a
								
							
						
					
					
						commit
						4fbab08586
					
				@ -394,9 +394,13 @@ class BitcoinRoutes {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private async getBlockAuditSummary(req: Request, res: Response) {
 | 
					  private async getBlockAuditSummary(req: Request, res: Response) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const transactions = await blocks.$getBlockAuditSummary(req.params.hash);
 | 
					      const auditSummary = await blocks.$getBlockAuditSummary(req.params.hash);
 | 
				
			||||||
      res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
 | 
					      if (auditSummary) {
 | 
				
			||||||
      res.json(transactions);
 | 
					        res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
 | 
				
			||||||
 | 
					        res.json(auditSummary);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return res.status(404).send(`audit not available`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      res.status(500).send(e instanceof Error ? e.message : e);
 | 
					      res.status(500).send(e instanceof Error ? e.message : e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1007,19 +1007,11 @@ class Blocks {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $getBlockAuditSummary(hash: string): Promise<any> {
 | 
					  public async $getBlockAuditSummary(hash: string): Promise<any> {
 | 
				
			||||||
    let summary;
 | 
					 | 
				
			||||||
    if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
 | 
					    if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
 | 
				
			||||||
      summary = await BlocksAuditsRepository.$getBlockAudit(hash);
 | 
					      return BlocksAuditsRepository.$getBlockAudit(hash);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // fallback to non-audited transaction summary
 | 
					 | 
				
			||||||
    if (!summary?.transactions?.length) {
 | 
					 | 
				
			||||||
      const strippedTransactions = await this.$getStrippedBlockTransactions(hash);
 | 
					 | 
				
			||||||
      summary = {
 | 
					 | 
				
			||||||
        transactions: strippedTransactions
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return summary;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public getLastDifficultyAdjustmentTime(): number {
 | 
					  public getLastDifficultyAdjustmentTime(): number {
 | 
				
			||||||
 | 
				
			|||||||
@ -64,7 +64,6 @@ class BlocksAuditRepositories {
 | 
				
			|||||||
      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.height, blocks.hash as id, UNIX_TIMESTAMP(blocks.blockTimestamp) as timestamp, blocks.size,
 | 
				
			||||||
        blocks.weight, blocks.tx_count,
 | 
					        blocks.weight, blocks.tx_count,
 | 
				
			||||||
        transactions,
 | 
					 | 
				
			||||||
        template,
 | 
					        template,
 | 
				
			||||||
        missing_txs as missingTxs,
 | 
					        missing_txs as missingTxs,
 | 
				
			||||||
        added_txs as addedTxs,
 | 
					        added_txs as addedTxs,
 | 
				
			||||||
@ -76,7 +75,6 @@ class BlocksAuditRepositories {
 | 
				
			|||||||
        FROM blocks_audits
 | 
					        FROM blocks_audits
 | 
				
			||||||
        JOIN blocks ON blocks.hash = blocks_audits.hash
 | 
					        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
 | 
				
			||||||
        JOIN blocks_summaries ON blocks_summaries.id = blocks_audits.hash
 | 
					 | 
				
			||||||
        WHERE blocks_audits.hash = "${hash}"
 | 
					        WHERE blocks_audits.hash = "${hash}"
 | 
				
			||||||
      `);
 | 
					      `);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
@ -85,12 +83,9 @@ class BlocksAuditRepositories {
 | 
				
			|||||||
        rows[0].addedTxs = JSON.parse(rows[0].addedTxs);
 | 
					        rows[0].addedTxs = JSON.parse(rows[0].addedTxs);
 | 
				
			||||||
        rows[0].freshTxs = JSON.parse(rows[0].freshTxs);
 | 
					        rows[0].freshTxs = JSON.parse(rows[0].freshTxs);
 | 
				
			||||||
        rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs);
 | 
					        rows[0].sigopTxs = JSON.parse(rows[0].sigopTxs);
 | 
				
			||||||
        rows[0].transactions = JSON.parse(rows[0].transactions);
 | 
					 | 
				
			||||||
        rows[0].template = JSON.parse(rows[0].template);
 | 
					        rows[0].template = JSON.parse(rows[0].template);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rows[0].transactions.length) {
 | 
					        return rows[0];
 | 
				
			||||||
          return rows[0];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    } catch (e: any) {
 | 
					    } catch (e: any) {
 | 
				
			||||||
 | 
				
			|||||||
@ -63,7 +63,7 @@
 | 
				
			|||||||
                    *ngIf="blockAudit?.matchRate != null; else nullHealth"
 | 
					                    *ngIf="blockAudit?.matchRate != null; else nullHealth"
 | 
				
			||||||
                  >{{ blockAudit?.matchRate }}%</span>
 | 
					                  >{{ blockAudit?.matchRate }}%</span>
 | 
				
			||||||
                  <ng-template #nullHealth>
 | 
					                  <ng-template #nullHealth>
 | 
				
			||||||
                    <ng-container *ngIf="!isLoadingAudit; else loadingHealth">
 | 
					                    <ng-container *ngIf="!isLoadingOverview; else loadingHealth">
 | 
				
			||||||
                      <span class="health-badge badge badge-secondary" i18n="unknown">Unknown</span>
 | 
					                      <span class="health-badge badge badge-secondary" i18n="unknown">Unknown</span>
 | 
				
			||||||
                    </ng-container>
 | 
					                    </ng-container>
 | 
				
			||||||
                  </ng-template>
 | 
					                  </ng-template>
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,9 @@ import { Component, OnInit, OnDestroy, ViewChildren, QueryList } from '@angular/
 | 
				
			|||||||
import { Location } from '@angular/common';
 | 
					import { Location } from '@angular/common';
 | 
				
			||||||
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
 | 
					import { ActivatedRoute, ParamMap, Router } from '@angular/router';
 | 
				
			||||||
import { ElectrsApiService } from '../../services/electrs-api.service';
 | 
					import { ElectrsApiService } from '../../services/electrs-api.service';
 | 
				
			||||||
import { switchMap, tap, throttleTime, catchError, map, shareReplay, startWith, pairwise, filter } from 'rxjs/operators';
 | 
					import { switchMap, tap, throttleTime, catchError, map, shareReplay, startWith } from 'rxjs/operators';
 | 
				
			||||||
import { Transaction, Vout } from '../../interfaces/electrs.interface';
 | 
					import { Transaction, Vout } from '../../interfaces/electrs.interface';
 | 
				
			||||||
import { Observable, of, Subscription, asyncScheduler, EMPTY, combineLatest } from 'rxjs';
 | 
					import { Observable, of, Subscription, asyncScheduler, EMPTY, combineLatest, forkJoin } from 'rxjs';
 | 
				
			||||||
import { StateService } from '../../services/state.service';
 | 
					import { StateService } from '../../services/state.service';
 | 
				
			||||||
import { SeoService } from '../../services/seo.service';
 | 
					import { SeoService } from '../../services/seo.service';
 | 
				
			||||||
import { WebsocketService } from '../../services/websocket.service';
 | 
					import { WebsocketService } from '../../services/websocket.service';
 | 
				
			||||||
@ -44,7 +44,6 @@ export class BlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  strippedTransactions: TransactionStripped[];
 | 
					  strippedTransactions: TransactionStripped[];
 | 
				
			||||||
  overviewTransitionDirection: string;
 | 
					  overviewTransitionDirection: string;
 | 
				
			||||||
  isLoadingOverview = true;
 | 
					  isLoadingOverview = true;
 | 
				
			||||||
  isLoadingAudit = true;
 | 
					 | 
				
			||||||
  error: any;
 | 
					  error: any;
 | 
				
			||||||
  blockSubsidy: number;
 | 
					  blockSubsidy: number;
 | 
				
			||||||
  fees: number;
 | 
					  fees: number;
 | 
				
			||||||
@ -281,143 +280,111 @@ export class BlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      this.isLoadingOverview = false;
 | 
					      this.isLoadingOverview = false;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!this.auditSupported) {
 | 
					    this.overviewSubscription = block$.pipe(
 | 
				
			||||||
      this.overviewSubscription = block$.pipe(
 | 
					      switchMap((block) => {
 | 
				
			||||||
        startWith(null),
 | 
					        return forkJoin([
 | 
				
			||||||
        pairwise(),
 | 
					          this.apiService.getStrippedBlockTransactions$(block.id)
 | 
				
			||||||
        switchMap(([prevBlock, block]) => this.apiService.getStrippedBlockTransactions$(block.id)
 | 
					 | 
				
			||||||
          .pipe(
 | 
					 | 
				
			||||||
            catchError((err) => {
 | 
					 | 
				
			||||||
              this.overviewError = err;
 | 
					 | 
				
			||||||
              return of([]);
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
            switchMap((transactions) => {
 | 
					 | 
				
			||||||
              if (prevBlock) {
 | 
					 | 
				
			||||||
                return of({ transactions, direction: (prevBlock.height < block.height) ? 'right' : 'left' });
 | 
					 | 
				
			||||||
              } else {
 | 
					 | 
				
			||||||
                return of({ transactions, direction: 'down' });
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
          )
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
      .subscribe(({transactions, direction}: {transactions: TransactionStripped[], direction: string}) => {
 | 
					 | 
				
			||||||
        this.strippedTransactions = transactions;
 | 
					 | 
				
			||||||
        this.isLoadingOverview = false;
 | 
					 | 
				
			||||||
        this.setupBlockGraphs();
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      (error) => {
 | 
					 | 
				
			||||||
        this.error = error;
 | 
					 | 
				
			||||||
        this.isLoadingOverview = false;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (this.auditSupported) {
 | 
					 | 
				
			||||||
      this.auditSubscription = block$.pipe(
 | 
					 | 
				
			||||||
        startWith(null),
 | 
					 | 
				
			||||||
        pairwise(),
 | 
					 | 
				
			||||||
        switchMap(([prevBlock, block]) => {
 | 
					 | 
				
			||||||
          this.isLoadingAudit = true;
 | 
					 | 
				
			||||||
          this.blockAudit = null;
 | 
					 | 
				
			||||||
          return this.apiService.getBlockAudit$(block.id)
 | 
					 | 
				
			||||||
            .pipe(
 | 
					            .pipe(
 | 
				
			||||||
              catchError((err) => {
 | 
					              catchError((err) => {
 | 
				
			||||||
                this.overviewError = err;
 | 
					                this.overviewError = err;
 | 
				
			||||||
                this.isLoadingAudit = false;
 | 
					                return of(null);
 | 
				
			||||||
                return of([]);
 | 
					 | 
				
			||||||
              })
 | 
					              })
 | 
				
			||||||
            );
 | 
					            ),
 | 
				
			||||||
        }
 | 
					          !this.isAuditAvailableFromBlockHeight(block.height) ? of(null) : this.apiService.getBlockAudit$(block.id)
 | 
				
			||||||
        ),
 | 
					            .pipe(
 | 
				
			||||||
        filter((response) => response != null),
 | 
					              catchError((err) => {
 | 
				
			||||||
        map((response) => {
 | 
					                this.overviewError = err;
 | 
				
			||||||
          const blockAudit = response.body;
 | 
					                return of(null);
 | 
				
			||||||
          const inTemplate = {};
 | 
					              })
 | 
				
			||||||
          const inBlock = {};
 | 
					            )
 | 
				
			||||||
          const isAdded = {};
 | 
					        ]);
 | 
				
			||||||
          const isCensored = {};
 | 
					      })
 | 
				
			||||||
          const isMissing = {};
 | 
					    )
 | 
				
			||||||
          const isSelected = {};
 | 
					    .subscribe(([transactions, blockAudit]) => {      
 | 
				
			||||||
          const isFresh = {};
 | 
					      if (transactions) {
 | 
				
			||||||
          const isSigop = {};
 | 
					        this.strippedTransactions = transactions;
 | 
				
			||||||
          this.numMissing = 0;
 | 
					      } else {
 | 
				
			||||||
          this.numUnexpected = 0;
 | 
					        this.strippedTransactions = [];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (blockAudit?.template) {
 | 
					      this.blockAudit = null;
 | 
				
			||||||
            for (const tx of blockAudit.template) {
 | 
					      if (transactions && blockAudit) {
 | 
				
			||||||
              inTemplate[tx.txid] = true;
 | 
					        const inTemplate = {};
 | 
				
			||||||
            }
 | 
					        const inBlock = {};
 | 
				
			||||||
            for (const tx of blockAudit.transactions) {
 | 
					        const isAdded = {};
 | 
				
			||||||
              inBlock[tx.txid] = true;
 | 
					        const isCensored = {};
 | 
				
			||||||
            }
 | 
					        const isMissing = {};
 | 
				
			||||||
            for (const txid of blockAudit.addedTxs) {
 | 
					        const isSelected = {};
 | 
				
			||||||
              isAdded[txid] = true;
 | 
					        const isFresh = {};
 | 
				
			||||||
            }
 | 
					        const isSigop = {};
 | 
				
			||||||
            for (const txid of blockAudit.missingTxs) {
 | 
					        this.numMissing = 0;
 | 
				
			||||||
              isCensored[txid] = true;
 | 
					        this.numUnexpected = 0;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const txid of blockAudit.freshTxs || []) {
 | 
					 | 
				
			||||||
              isFresh[txid] = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const txid of blockAudit.sigopTxs || []) {
 | 
					 | 
				
			||||||
              isSigop[txid] = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // set transaction statuses
 | 
					 | 
				
			||||||
            for (const tx of blockAudit.template) {
 | 
					 | 
				
			||||||
              tx.context = 'projected';
 | 
					 | 
				
			||||||
              if (isCensored[tx.txid]) {
 | 
					 | 
				
			||||||
                tx.status = 'censored';
 | 
					 | 
				
			||||||
              } else if (inBlock[tx.txid]) {
 | 
					 | 
				
			||||||
                tx.status = 'found';
 | 
					 | 
				
			||||||
              } else {
 | 
					 | 
				
			||||||
                tx.status = isFresh[tx.txid] ? 'fresh' : (isSigop[tx.txid] ? 'sigop' : 'missing');
 | 
					 | 
				
			||||||
                isMissing[tx.txid] = true;
 | 
					 | 
				
			||||||
                this.numMissing++;
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const [index, tx] of blockAudit.transactions.entries()) {
 | 
					 | 
				
			||||||
              tx.context = 'actual';
 | 
					 | 
				
			||||||
              if (index === 0) {
 | 
					 | 
				
			||||||
                tx.status = null;
 | 
					 | 
				
			||||||
              } else if (isAdded[tx.txid]) {
 | 
					 | 
				
			||||||
                tx.status = 'added';
 | 
					 | 
				
			||||||
              } else if (inTemplate[tx.txid]) {
 | 
					 | 
				
			||||||
                tx.status = 'found';
 | 
					 | 
				
			||||||
              } else {
 | 
					 | 
				
			||||||
                tx.status = 'selected';
 | 
					 | 
				
			||||||
                isSelected[tx.txid] = true;
 | 
					 | 
				
			||||||
                this.numUnexpected++;
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const tx of blockAudit.transactions) {
 | 
					 | 
				
			||||||
              inBlock[tx.txid] = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - this.block.extras.totalFees) / blockAudit.expectedFees : 0;
 | 
					        if (blockAudit?.template) {
 | 
				
			||||||
            blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block.weight) / blockAudit.expectedWeight : 0;
 | 
					          for (const tx of blockAudit.template) {
 | 
				
			||||||
            blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block.tx_count) / blockAudit.template.length : 0;
 | 
					            inTemplate[tx.txid] = true;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.setAuditAvailable(true);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            this.setAuditAvailable(false);
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          return blockAudit;
 | 
					          for (const tx of transactions) {
 | 
				
			||||||
        }),
 | 
					            inBlock[tx.txid] = true;
 | 
				
			||||||
        catchError((err) => {
 | 
					          }
 | 
				
			||||||
          console.log(err);
 | 
					          for (const txid of blockAudit.addedTxs) {
 | 
				
			||||||
          this.error = err;
 | 
					            isAdded[txid] = true;
 | 
				
			||||||
          this.isLoadingOverview = false;
 | 
					          }
 | 
				
			||||||
          this.isLoadingAudit = false;
 | 
					          for (const txid of blockAudit.missingTxs) {
 | 
				
			||||||
 | 
					            isCensored[txid] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          for (const txid of blockAudit.freshTxs || []) {
 | 
				
			||||||
 | 
					            isFresh[txid] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          for (const txid of blockAudit.sigopTxs || []) {
 | 
				
			||||||
 | 
					            isSigop[txid] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          // set transaction statuses
 | 
				
			||||||
 | 
					          for (const tx of blockAudit.template) {
 | 
				
			||||||
 | 
					            tx.context = 'projected';
 | 
				
			||||||
 | 
					            if (isCensored[tx.txid]) {
 | 
				
			||||||
 | 
					              tx.status = 'censored';
 | 
				
			||||||
 | 
					            } else if (inBlock[tx.txid]) {
 | 
				
			||||||
 | 
					              tx.status = 'found';
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              tx.status = isFresh[tx.txid] ? 'fresh' : (isSigop[tx.txid] ? 'sigop' : 'missing');
 | 
				
			||||||
 | 
					              isMissing[tx.txid] = true;
 | 
				
			||||||
 | 
					              this.numMissing++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          for (const [index, tx] of transactions.entries()) {
 | 
				
			||||||
 | 
					            tx.context = 'actual';
 | 
				
			||||||
 | 
					            if (index === 0) {
 | 
				
			||||||
 | 
					              tx.status = null;
 | 
				
			||||||
 | 
					            } else if (isAdded[tx.txid]) {
 | 
				
			||||||
 | 
					              tx.status = 'added';
 | 
				
			||||||
 | 
					            } else if (inTemplate[tx.txid]) {
 | 
				
			||||||
 | 
					              tx.status = 'found';
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              tx.status = 'selected';
 | 
				
			||||||
 | 
					              isSelected[tx.txid] = true;
 | 
				
			||||||
 | 
					              this.numUnexpected++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          for (const tx of transactions) {
 | 
				
			||||||
 | 
					            inBlock[tx.txid] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          blockAudit.feeDelta = blockAudit.expectedFees > 0 ? (blockAudit.expectedFees - this.block.extras.totalFees) / blockAudit.expectedFees : 0;
 | 
				
			||||||
 | 
					          blockAudit.weightDelta = blockAudit.expectedWeight > 0 ? (blockAudit.expectedWeight - this.block.weight) / blockAudit.expectedWeight : 0;
 | 
				
			||||||
 | 
					          blockAudit.txDelta = blockAudit.template.length > 0 ? (blockAudit.template.length - this.block.tx_count) / blockAudit.template.length : 0;
 | 
				
			||||||
 | 
					          this.blockAudit = blockAudit;
 | 
				
			||||||
 | 
					          this.setAuditAvailable(true);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
          this.setAuditAvailable(false);
 | 
					          this.setAuditAvailable(false);
 | 
				
			||||||
          return of(null);
 | 
					        }
 | 
				
			||||||
        }),
 | 
					      } else {
 | 
				
			||||||
      ).subscribe((blockAudit) => {
 | 
					        this.setAuditAvailable(false);
 | 
				
			||||||
        this.blockAudit = blockAudit;
 | 
					      }
 | 
				
			||||||
        this.setupBlockGraphs();
 | 
					
 | 
				
			||||||
        this.isLoadingOverview = false;
 | 
					      this.isLoadingOverview = false;
 | 
				
			||||||
        this.isLoadingAudit = false;
 | 
					      this.setupBlockGraphs();
 | 
				
			||||||
      });
 | 
					    });
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.networkChangedSubscription = this.stateService.networkChanged$
 | 
					    this.networkChangedSubscription = this.stateService.networkChanged$
 | 
				
			||||||
      .subscribe((network) => this.network = network);
 | 
					      .subscribe((network) => this.network = network);
 | 
				
			||||||
@ -652,25 +619,32 @@ export class BlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  updateAuditAvailableFromBlockHeight(blockHeight: number): void {
 | 
					  updateAuditAvailableFromBlockHeight(blockHeight: number): void {
 | 
				
			||||||
    if (!this.auditSupported) {
 | 
					    if (!this.isAuditAvailableFromBlockHeight(blockHeight)) {
 | 
				
			||||||
      this.setAuditAvailable(false);
 | 
					      this.setAuditAvailable(false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  isAuditAvailableFromBlockHeight(blockHeight: number): boolean {
 | 
				
			||||||
 | 
					    if (!this.auditSupported) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    switch (this.stateService.network) {
 | 
					    switch (this.stateService.network) {
 | 
				
			||||||
      case 'testnet':
 | 
					      case 'testnet':
 | 
				
			||||||
        if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) {
 | 
					        if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) {
 | 
				
			||||||
          this.setAuditAvailable(false);
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'signet':
 | 
					      case 'signet':
 | 
				
			||||||
        if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) {
 | 
					        if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) {
 | 
				
			||||||
          this.setAuditAvailable(false);
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) {
 | 
					        if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) {
 | 
				
			||||||
          this.setAuditAvailable(false);
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getMinBlockFee(block: BlockExtended): number {
 | 
					  getMinBlockFee(block: BlockExtended): number {
 | 
				
			||||||
 | 
				
			|||||||
@ -153,6 +153,8 @@ export interface BlockExtended extends Block {
 | 
				
			|||||||
export interface BlockAudit extends BlockExtended {
 | 
					export interface BlockAudit extends BlockExtended {
 | 
				
			||||||
  missingTxs: string[],
 | 
					  missingTxs: string[],
 | 
				
			||||||
  addedTxs: string[],
 | 
					  addedTxs: string[],
 | 
				
			||||||
 | 
					  freshTxs: string[],
 | 
				
			||||||
 | 
					  sigopTxs: string[],
 | 
				
			||||||
  matchRate: number,
 | 
					  matchRate: number,
 | 
				
			||||||
  expectedFees: number,
 | 
					  expectedFees: number,
 | 
				
			||||||
  expectedWeight: number,
 | 
					  expectedWeight: number,
 | 
				
			||||||
@ -169,6 +171,7 @@ export interface TransactionStripped {
 | 
				
			|||||||
  vsize: number;
 | 
					  vsize: number;
 | 
				
			||||||
  value: number;
 | 
					  value: number;
 | 
				
			||||||
  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected';
 | 
					  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected';
 | 
				
			||||||
 | 
					  context?: 'projected' | 'actual';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface RbfTransaction extends TransactionStripped {
 | 
					interface RbfTransaction extends TransactionStripped {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { Injectable } from '@angular/core';
 | 
					import { Injectable } from '@angular/core';
 | 
				
			||||||
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
 | 
					import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
 | 
				
			||||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
 | 
					import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
 | 
				
			||||||
  PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree } from '../interfaces/node-api.interface';
 | 
					  PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit } from '../interfaces/node-api.interface';
 | 
				
			||||||
import { Observable } from 'rxjs';
 | 
					import { Observable } from 'rxjs';
 | 
				
			||||||
import { StateService } from './state.service';
 | 
					import { StateService } from './state.service';
 | 
				
			||||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
					import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
				
			||||||
@ -245,9 +245,9 @@ export class ApiService {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getBlockAudit$(hash: string) : Observable<any> {
 | 
					  getBlockAudit$(hash: string) : Observable<BlockAudit> {
 | 
				
			||||||
    return this.httpClient.get<any>(
 | 
					    return this.httpClient.get<BlockAudit>(
 | 
				
			||||||
      this.apiBaseUrl + this.apiBasePath + `/api/v1/block/${hash}/audit-summary`, { observe: 'response' }
 | 
					      this.apiBaseUrl + this.apiBasePath + `/api/v1/block/${hash}/audit-summary`
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user