diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index d86ecf665..d429299e1 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -373,7 +373,7 @@ class NodesApi { public async $searchNodeByPublicKeyOrAlias(search: string) { try { - const publicKeySearch = search.replace('%', '') + '%'; + const publicKeySearch = search.replace(/[^a-zA-Z0-9]/g, '') + '%'; const aliasSearch = search .replace(/[-_.]/g, ' ') // Replace all -_. characters with empty space. Eg: "ln.nicehash" becomes "ln nicehash". .replace(/[^a-zA-Z0-9 ]/g, '') // Remove all special characters and keep just A to Z, 0 to 9. diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index e3543f4fc..70635237f 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -186,6 +186,12 @@ class Mempool { loadingIndicators.setProgress('mempool', progress); loggerTimer = new Date().getTime() / 1000; } + // Break and restart mempool loop if we spend too much time processing + // new transactions that may lead to falling behind on block height + if (this.inSync && (new Date().getTime()) - start > 10_000) { + logger.debug('Breaking mempool loop because the 10s time limit exceeded.'); + break; + } } // Reset esplora 404 counter and log a warning if needed diff --git a/frontend/cypress/e2e/mainnet/mainnet.spec.ts b/frontend/cypress/e2e/mainnet/mainnet.spec.ts index da9e00b9f..b12b93cf1 100644 --- a/frontend/cypress/e2e/mainnet/mainnet.spec.ts +++ b/frontend/cypress/e2e/mainnet/mainnet.spec.ts @@ -537,7 +537,7 @@ describe('Mainnet', () => { cy.get('.container-xl > :nth-child(3)').invoke('css', 'width').should('equal', alertWidth); }); - cy.get('.btn-danger').then(getRectangle).then((rectA) => { + cy.get('.btn-warning').then(getRectangle).then((rectA) => { cy.get('.alert').then(getRectangle).then((rectB) => { expect(areOverlapping(rectA, rectB), 'Confirmations box and RBF alert are overlapping').to.be.false; }); @@ -582,7 +582,7 @@ describe('Mainnet', () => { cy.get(alertLocator).invoke('css', 'width').should('equal', firstWidth); }); - cy.get('.btn-danger').then(getRectangle).then((rectA) => { + cy.get('.btn-warning').then(getRectangle).then((rectA) => { cy.get('.alert').then(getRectangle).then((rectB) => { expect(areOverlapping(rectA, rectB), 'Confirmations box and RBF alert are overlapping').to.be.false; }); diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 2ee611bc6..39fbb95e0 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -44,21 +44,16 @@ {{ auditScores[block.id] }}% + >{{ block.extras.matchRate }}% - - Unknown - - - - + Unknown diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index 8b4aa38e7..324807628 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -1,6 +1,6 @@ -import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; -import { BehaviorSubject, combineLatest, concat, Observable, timer, EMPTY, Subscription, of } from 'rxjs'; -import { catchError, delayWhen, map, retryWhen, scan, skip, switchMap, tap } from 'rxjs/operators'; +import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; +import { BehaviorSubject, combineLatest, Observable, timer, of } from 'rxjs'; +import { delayWhen, map, retryWhen, scan, switchMap, tap } from 'rxjs/operators'; import { BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; @@ -12,19 +12,14 @@ import { WebsocketService } from '../../services/websocket.service'; styleUrls: ['./blocks-list.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class BlocksList implements OnInit, OnDestroy { +export class BlocksList implements OnInit { @Input() widget: boolean = false; blocks$: Observable = undefined; - auditScores: { [hash: string]: number | void } = {}; - - auditScoreSubscription: Subscription; - latestScoreSubscription: Subscription; indexingAvailable = false; auditAvailable = false; isLoading = true; - loadingScores = true; fromBlockHeight = undefined; paginationMaxSize: number; page = 1; @@ -66,7 +61,7 @@ export class BlocksList implements OnInit, OnDestroy { this.blocksCount = blocks[0].height + 1; } this.isLoading = false; - this.lastBlockHeight = Math.max(...blocks.map(o => o.height)) + this.lastBlockHeight = Math.max(...blocks.map(o => o.height)); }), map(blocks => { if (this.indexingAvailable) { @@ -82,7 +77,7 @@ export class BlocksList implements OnInit, OnDestroy { return blocks; }), retryWhen(errors => errors.pipe(delayWhen(() => timer(10000)))) - ) + ); }) ), this.stateService.blocks$ @@ -121,68 +116,17 @@ export class BlocksList implements OnInit, OnDestroy { return of(blocks); }) ); - - if (this.indexingAvailable && this.auditAvailable) { - this.auditScoreSubscription = this.fromHeightSubject.pipe( - switchMap((fromBlockHeight) => { - this.loadingScores = true; - return this.apiService.getBlockAuditScores$(this.page === 1 ? undefined : fromBlockHeight) - .pipe( - catchError(() => { - return EMPTY; - }) - ); - }) - ).subscribe((scores) => { - Object.values(scores).forEach(score => { - this.auditScores[score.hash] = score?.matchRate != null ? score.matchRate : null; - }); - this.loadingScores = false; - this.cd.markForCheck(); - }); - - this.latestScoreSubscription = this.stateService.blocks$.pipe( - switchMap((block) => { - if (block[0]?.extras?.matchRate != null) { - return of({ - hash: block[0].id, - matchRate: block[0]?.extras?.matchRate, - }); - } - else if (block[0]?.id && this.auditScores[block[0].id] === undefined) { - return this.apiService.getBlockAuditScore$(block[0].id) - .pipe( - catchError(() => { - return EMPTY; - }) - ); - } else { - return EMPTY; - } - }), - ).subscribe((score) => { - if (score && score.hash) { - this.auditScores[score.hash] = score?.matchRate != null ? score.matchRate : null; - this.cd.markForCheck(); - } - }); - } } - ngOnDestroy(): void { - this.auditScoreSubscription?.unsubscribe(); - this.latestScoreSubscription?.unsubscribe(); - } - - pageChange(page: number) { + pageChange(page: number): void { this.fromHeightSubject.next((this.blocksCount - 1) - (page - 1) * 15); } - trackByBlock(index: number, block: BlockExtended) { + trackByBlock(index: number, block: BlockExtended): number { return block.height; } - isEllipsisActive(e) { + isEllipsisActive(e): boolean { return (e.offsetWidth < e.scrollWidth); } } \ No newline at end of file diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 64eeeaecd..a5a44ee90 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -18,7 +18,12 @@
- +
diff --git a/frontend/src/app/shared/components/confirmations/confirmations.component.html b/frontend/src/app/shared/components/confirmations/confirmations.component.html index 1d7138c7c..e785ce4dd 100644 --- a/frontend/src/app/shared/components/confirmations/confirmations.component.html +++ b/frontend/src/app/shared/components/confirmations/confirmations.component.html @@ -6,8 +6,11 @@ - + - + + + + \ No newline at end of file diff --git a/frontend/src/app/shared/components/confirmations/confirmations.component.ts b/frontend/src/app/shared/components/confirmations/confirmations.component.ts index 8d14128e5..624c58278 100644 --- a/frontend/src/app/shared/components/confirmations/confirmations.component.ts +++ b/frontend/src/app/shared/components/confirmations/confirmations.component.ts @@ -11,6 +11,7 @@ export class ConfirmationsComponent implements OnChanges { @Input() chainTip: number; @Input() height: number; @Input() replaced: boolean = false; + @Input() removed: boolean = false; @Input() hideUnconfirmed: boolean = false; @Input() buttonClass: string = ''; diff --git a/production/nginx/location-api.conf b/production/nginx/location-api.conf index 638e1911c..2b2b85411 100644 --- a/production/nginx/location-api.conf +++ b/production/nginx/location-api.conf @@ -97,10 +97,10 @@ location @mempool-api-v1-cache-normal { proxy_set_header X-Forwarded-Proto $scheme; proxy_cache api; - proxy_cache_valid 200 10s; + proxy_cache_valid 200 2s; proxy_redirect off; - expires 10s; + expires 2s; } location @mempool-api-v1-cache-disabled {