Display basic mining info on transaction page
This commit is contained in:
		
							parent
							
								
									29bbb922c5
								
							
						
					
					
						commit
						c17b77fe31
					
				| @ -70,6 +70,25 @@ | ||||
|                     <app-tx-features [tx]="tx"></app-tx-features> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|                 <tr *ngIf="network === ''"> | ||||
|                   <td class="td-width" i18n="transaction.mining">Mining</td> | ||||
|                   <td *ngIf="pool" class="wrap-cell"> | ||||
|                     <a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pool.slug]" class="badge mr-1" | ||||
|                       [class]="pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'"> | ||||
|                       {{ pool.name }} | ||||
|                     </a> | ||||
|                     <ng-container *ngIf="auditStatus"> | ||||
|                       <span *ngIf="auditStatus.expected; else seen" class="badge badge-success mr-1" i18n-ngbTooltip="Expected in block tooltip" ngbTooltip="This transaction was projected to be included in the block" placement="bottom" i18n="tx-features.tag.expected|Expected in Block">Expected in Block</span> | ||||
|                       <ng-template #seen><span *ngIf="auditStatus.seen; else notSeen" class="badge badge-success mr-1" i18n-ngbTooltip="Seen in mempool tooltip" ngbTooltip="This transaction was seen in the mempool prior to mining" placement="bottom" i18n="tx-features.tag.seen|Seen in Mempool">Seen in Mempool</span></ng-template> | ||||
|                       <ng-template #notSeen><span class="badge badge-warning mr-1" i18n-ngbTooltip="Not seen in mempool tooltip" ngbTooltip="This transaction was missing from our mempool prior to mining" placement="bottom" i18n="tx-features.tag.not-seen|Not seen in Mempool">Not seen in Mempool</span></ng-template> | ||||
|                       <span *ngIf="auditStatus.added" class="badge badge-primary mr-1" i18n-ngbTooltip="Added transaction tooltip" ngbTooltip="This transaction may have been added or prioritized out-of-band" placement="bottom" i18n="tx-features.tag.added|Added">Added</span> | ||||
|                       <span *ngIf="auditStatus.conflict" class="badge badge-warning mr-1" i18n-ngbTooltip="Conflict in mempool tooltip" ngbTooltip="This transaction conflicted with another version in our mempool" placement="bottom" i18n="tx-features.tag.conflict|Conflict">Conflict</span> | ||||
|                     </ng-container> | ||||
|                   </td> | ||||
|                   <td *ngIf="!pool"> | ||||
|                     <span class="skeleton-loader"></span> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               </tbody> | ||||
|             </table> | ||||
|           </div> | ||||
| @ -509,7 +528,7 @@ | ||||
| <ng-template #feeTable> | ||||
|   <table class="table table-borderless table-striped"> | ||||
|     <tbody> | ||||
|       <tr *ngIf="isMobile && (network === 'liquid' || network === 'liquidtestnet' || !featuresEnabled)"></tr> | ||||
|       <tr *ngIf="isMobile && (network === 'liquid' || network === 'liquidtestnet' || !featuresEnabled || network === '')"></tr> | ||||
|       <tr> | ||||
|         <td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td> | ||||
|         <td>{{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [blockConversion]="blockConversion" [value]="tx.fee"></app-fiat></span></td> | ||||
|  | ||||
| @ -149,6 +149,10 @@ | ||||
| 		.btn { | ||||
| 			display: block; | ||||
| 		} | ||||
| 
 | ||||
|     &.wrap-cell { | ||||
|       white-space: normal; | ||||
|     } | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -8,10 +8,11 @@ import { | ||||
|   retryWhen, | ||||
|   delay, | ||||
|   mergeMap, | ||||
|   tap | ||||
|   tap, | ||||
|   map | ||||
| } from 'rxjs/operators'; | ||||
| import { Transaction } from '../../interfaces/electrs.interface'; | ||||
| import { of, merge, Subscription, Observable, Subject, from, throwError } from 'rxjs'; | ||||
| import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest } from 'rxjs'; | ||||
| import { StateService } from '../../services/state.service'; | ||||
| import { CacheService } from '../../services/cache.service'; | ||||
| import { WebsocketService } from '../../services/websocket.service'; | ||||
| @ -28,6 +29,21 @@ import { isFeatureActive } from '../../bitcoin.utils'; | ||||
| import { ServicesApiServices } from '../../services/services-api.service'; | ||||
| import { EnterpriseService } from '../../services/enterprise.service'; | ||||
| 
 | ||||
| interface Pool { | ||||
|   id: number; | ||||
|   name: string; | ||||
|   slug: string; | ||||
| } | ||||
| 
 | ||||
| interface AuditStatus { | ||||
|   seen: boolean; | ||||
|   expected: boolean; | ||||
|   added: boolean; | ||||
|   delayed?: number; | ||||
|   accelerated: boolean; | ||||
|   conflict: boolean; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-transaction', | ||||
|   templateUrl: './transaction.component.html', | ||||
| @ -58,6 +74,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|   urlFragmentSubscription: Subscription; | ||||
|   mempoolBlocksSubscription: Subscription; | ||||
|   blocksSubscription: Subscription; | ||||
|   miningSubscription: Subscription; | ||||
|   fragmentParams: URLSearchParams; | ||||
|   rbfTransaction: undefined | Transaction; | ||||
|   replaced: boolean = false; | ||||
| @ -67,11 +84,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|   accelerationInfo: Acceleration | null = null; | ||||
|   sigops: number | null; | ||||
|   adjustedVsize: number | null; | ||||
|   pool: Pool | null; | ||||
|   auditStatus: AuditStatus | null; | ||||
|   showCpfpDetails = false; | ||||
|   fetchCpfp$ = new Subject<string>(); | ||||
|   fetchRbfHistory$ = new Subject<string>(); | ||||
|   fetchCachedTx$ = new Subject<string>(); | ||||
|   fetchAcceleration$ = new Subject<string>(); | ||||
|   fetchMiningInfo$ = new Subject<{ hash: string, height: number, txid: string }>(); | ||||
|   isCached: boolean = false; | ||||
|   now = Date.now(); | ||||
|   da$: Observable<DifficultyAdjustment>; | ||||
| @ -100,6 +120,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|   acceleratorAvailable: boolean = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === ''; | ||||
|   showAccelerationSummary = false; | ||||
|   scrollIntoAccelPreview = false; | ||||
|   auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; | ||||
| 
 | ||||
|   @ViewChild('graphContainer') | ||||
|   graphContainer: ElementRef; | ||||
| @ -266,6 +287,52 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     this.miningSubscription = this.fetchMiningInfo$.pipe( | ||||
|       filter((target) => target.txid === this.txId), | ||||
|       tap(() => { | ||||
|         this.pool = null; | ||||
|         this.auditStatus = null; | ||||
|       }), | ||||
|       switchMap(({ hash, height, txid }) => { | ||||
|         const foundBlock = this.cacheService.getCachedBlock(height) || null; | ||||
|         const auditAvailable = this.isAuditAvailable(height); | ||||
|         return combineLatest([ | ||||
|           foundBlock ? of(foundBlock.extras.pool) : this.apiService.getBlock$(hash).pipe( | ||||
|             map(block => { | ||||
|               return block.extras.pool; | ||||
|             }), | ||||
|             catchError(() => { | ||||
|               return of(null); | ||||
|             }) | ||||
|           ), | ||||
|           auditAvailable ? this.apiService.getBlockAudit$(hash).pipe( | ||||
|             map(audit => { | ||||
|               const isAdded = audit.addedTxs.includes(txid); | ||||
|               const isAccelerated = audit.acceleratedTxs.includes(txid); | ||||
|               const isConflict = audit.fullrbfTxs.includes(txid); | ||||
|               const isExpected = audit.template.some(tx => tx.txid === txid); | ||||
|               return { | ||||
|                 seen: isExpected || !(isAdded || isConflict), | ||||
|                 expected: isExpected, | ||||
|                 added: isAdded, | ||||
|                 conflict: isConflict, | ||||
|                 accelerated: isAccelerated, | ||||
|               }; | ||||
|             }), | ||||
|             catchError(() => { | ||||
|               return of(null); | ||||
|             }) | ||||
|           ) : of(null) | ||||
|         ]); | ||||
|       }), | ||||
|       catchError(() => { | ||||
|         return of(null); | ||||
|       }) | ||||
|     ).subscribe(([pool, auditStatus]) => { | ||||
|       this.pool = pool; | ||||
|       this.auditStatus = auditStatus; | ||||
|     }); | ||||
| 
 | ||||
|     this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => { | ||||
|       this.now = Date.now(); | ||||
|       if (txPosition && txPosition.txid === this.txId && txPosition.position) { | ||||
| @ -396,6 +463,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|             } | ||||
|           } else { | ||||
|             this.fetchAcceleration$.next(tx.status.block_hash); | ||||
|             this.fetchMiningInfo$.next({ hash: tx.status.block_hash, height: tx.status.block_height, txid: tx.txid }); | ||||
|             this.transactionTime = 0; | ||||
|           } | ||||
| 
 | ||||
| @ -453,6 +521,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|           this.audioService.playSound('magic'); | ||||
|         } | ||||
|         this.fetchAcceleration$.next(block.id); | ||||
|         this.fetchMiningInfo$.next({ hash: block.id, height: block.height, txid: this.tx.txid }); | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
| @ -606,6 +675,29 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|     this.featuresEnabled = this.segwitEnabled || this.taprootEnabled || this.rbfEnabled; | ||||
|   } | ||||
| 
 | ||||
|   isAuditAvailable(blockHeight: number): boolean { | ||||
|     if (!this.auditEnabled) { | ||||
|       return false; | ||||
|     } | ||||
|     switch (this.stateService.network) { | ||||
|       case 'testnet': | ||||
|         if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) { | ||||
|           return false; | ||||
|         } | ||||
|         break; | ||||
|       case 'signet': | ||||
|         if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) { | ||||
|           return false; | ||||
|         } | ||||
|         break; | ||||
|       default: | ||||
|         if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) { | ||||
|           return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   resetTransaction() { | ||||
|     this.error = undefined; | ||||
|     this.tx = null; | ||||
| @ -625,6 +717,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|     this.accelerationInfo = null; | ||||
|     this.txInBlockIndex = null; | ||||
|     this.mempoolPosition = null; | ||||
|     this.pool = null; | ||||
|     this.auditStatus = null; | ||||
|     document.body.scrollTo(0, 0); | ||||
|     this.leaveTransaction(); | ||||
|   } | ||||
| @ -712,6 +806,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|     this.mempoolPositionSubscription.unsubscribe(); | ||||
|     this.mempoolBlocksSubscription.unsubscribe(); | ||||
|     this.blocksSubscription.unsubscribe(); | ||||
|     this.miningSubscription?.unsubscribe(); | ||||
|     this.leaveTransaction(); | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user