Merge branch 'master' into nymkappa/feature/ctrlf-addresses
This commit is contained in:
		
						commit
						9c73e5be78
					
				
							
								
								
									
										2
									
								
								.github/workflows/cypress.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/cypress.yml
									
									
									
									
										vendored
									
									
								
							| @ -21,7 +21,7 @@ jobs: | ||||
|       - name: Setup node | ||||
|         uses: actions/setup-node@v2 | ||||
|         with: | ||||
|           node-version: 16.10.0 | ||||
|           node-version: 16.15.0 | ||||
|           cache: 'npm' | ||||
|           cache-dependency-path: frontend/package-lock.json | ||||
|       - name: ${{ matrix.browser }} browser tests (Mempool) | ||||
|  | ||||
| @ -134,7 +134,7 @@ class Blocks { | ||||
|       blockExtended.extras.avgFeeRate = stats.avgfeerate; | ||||
|     } | ||||
| 
 | ||||
|     if (Common.indexingEnabled()) { | ||||
|     if (['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK)) { | ||||
|       let pool: PoolTag; | ||||
|       if (blockExtended.extras?.coinbaseTx !== undefined) { | ||||
|         pool = await this.$findBlockMiner(blockExtended.extras?.coinbaseTx); | ||||
| @ -143,7 +143,8 @@ class Blocks { | ||||
|       } | ||||
| 
 | ||||
|       if (!pool) { // We should never have this situation in practise
 | ||||
|         logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. Check your "pools" table entries`); | ||||
|         logger.warn(`Cannot assign pool to block ${blockExtended.height} and 'unknown' pool does not exist. ` + | ||||
|           `Check your "pools" table entries`); | ||||
|         return blockExtended; | ||||
|       } | ||||
| 
 | ||||
| @ -389,6 +390,42 @@ class Blocks { | ||||
|     return prepareBlock(blockExtended); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Index a block by hash if it's missing from the database. Returns the block after indexing | ||||
|    */ | ||||
|   public async $getBlock(hash: string): Promise<BlockExtended | IEsploraApi.Block> { | ||||
|     // Check the memory cache
 | ||||
|     const blockByHash = this.getBlocks().find((b) => b.id === hash); | ||||
|     if (blockByHash) { | ||||
|       return blockByHash; | ||||
|     } | ||||
| 
 | ||||
|     // Block has already been indexed
 | ||||
|     if (Common.indexingEnabled()) { | ||||
|       const dbBlock = await blocksRepository.$getBlockByHash(hash); | ||||
|       if (dbBlock != null) { | ||||
|         return prepareBlock(dbBlock); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const block = await bitcoinApi.$getBlock(hash); | ||||
| 
 | ||||
|     // Not Bitcoin network, return the block as it
 | ||||
|     if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { | ||||
|       return block; | ||||
|     } | ||||
| 
 | ||||
|     // Bitcoin network, add our custom data on top
 | ||||
|     const transactions = await this.$getTransactionsExtended(hash, block.height, true); | ||||
|     const blockExtended = await this.$getBlockExtended(block, transactions); | ||||
|     if (Common.indexingEnabled()) { | ||||
|       delete(blockExtended['coinbaseTx']); | ||||
|       await blocksRepository.$saveBlockInDatabase(blockExtended); | ||||
|     } | ||||
| 
 | ||||
|     return blockExtended; | ||||
|   } | ||||
| 
 | ||||
|   public async $getBlocksExtras(fromHeight?: number, limit: number = 15): Promise<BlockExtended[]> { | ||||
|     // Note - This API is breaking if indexing is not available. For now it is okay because we only
 | ||||
|     // use it for the mining pages, and mining pages should not be available if indexing is turned off.
 | ||||
|  | ||||
| @ -169,12 +169,12 @@ export class Common { | ||||
|       default: return null; | ||||
|     } | ||||
|   } | ||||
|    | ||||
| 
 | ||||
|   static indexingEnabled(): boolean { | ||||
|     return ( | ||||
|       ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && | ||||
|       ['mainnet', 'testnet', 'signet', 'regtest'].includes(config.MEMPOOL.NETWORK) && | ||||
|       config.DATABASE.ENABLED === true && | ||||
|       config.MEMPOOL.INDEXING_BLOCKS_AMOUNT != 0 | ||||
|       config.MEMPOOL.INDEXING_BLOCKS_AMOUNT !== 0 | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -350,7 +350,8 @@ class Server { | ||||
| 
 | ||||
|     this.app | ||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras', routes.getBlocksExtras) | ||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras); | ||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-extras/:height', routes.getBlocksExtras) | ||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock); | ||||
| 
 | ||||
|     if (config.MEMPOOL.BACKEND !== 'esplora') { | ||||
|       this.app | ||||
| @ -362,7 +363,6 @@ class Server { | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', routes.getRawTransaction) | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus) | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends) | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', routes.getBlock) | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', routes.getBlockHeader) | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'blocks', routes.getBlocks) | ||||
|         .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', routes.getBlocks) | ||||
|  | ||||
| @ -287,6 +287,7 @@ class BlocksRepository { | ||||
|         return null; | ||||
|       } | ||||
| 
 | ||||
|       rows[0].fee_span = JSON.parse(rows[0].fee_span); | ||||
|       return rows[0]; | ||||
|     } catch (e) { | ||||
|       logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e)); | ||||
| @ -294,6 +295,34 @@ class BlocksRepository { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Get one block by hash | ||||
|    */ | ||||
|   public async $getBlockByHash(hash: string): Promise<object | null> { | ||||
|     try { | ||||
|       const query = ` | ||||
|         SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, hash as id, | ||||
|         pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug, | ||||
|         pools.addresses as pool_addresses, pools.regexes as pool_regexes, | ||||
|         previous_block_hash as previousblockhash | ||||
|         FROM blocks | ||||
|         JOIN pools ON blocks.pool_id = pools.id | ||||
|         WHERE hash = '${hash}'; | ||||
|       `;
 | ||||
|       const [rows]: any[] = await DB.query(query); | ||||
| 
 | ||||
|       if (rows.length <= 0) { | ||||
|         return null; | ||||
|       } | ||||
| 
 | ||||
|       rows[0].fee_span = JSON.parse(rows[0].fee_span); | ||||
|       return rows[0]; | ||||
|     } catch (e) { | ||||
|       logger.err(`Cannot get indexed block ${hash}. Reason: ` + (e instanceof Error ? e.message : e)); | ||||
|       throw e; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Return blocks difficulty | ||||
|    */ | ||||
| @ -397,18 +426,28 @@ class BlocksRepository { | ||||
|       const [blocks]: any[] = await DB.query(`SELECT height, hash, previous_block_hash,
 | ||||
|         UNIX_TIMESTAMP(blockTimestamp) as timestamp FROM blocks ORDER BY height`);
 | ||||
| 
 | ||||
|       let currentHeight = 1; | ||||
|       while (currentHeight < blocks.length) { | ||||
|         if (blocks[currentHeight].previous_block_hash !== blocks[currentHeight - 1].hash) { | ||||
|           logger.warn(`Chain divergence detected at block ${blocks[currentHeight - 1].height}, re-indexing newer blocks and hashrates`); | ||||
|           await this.$deleteBlocksFrom(blocks[currentHeight - 1].height); | ||||
|           await HashratesRepository.$deleteHashratesFromTimestamp(blocks[currentHeight - 1].timestamp - 604800); | ||||
|       let partialMsg = false; | ||||
|       let idx = 1; | ||||
|       while (idx < blocks.length) { | ||||
|         if (blocks[idx].height - 1 !== blocks[idx - 1].height) { | ||||
|           if (partialMsg === false) { | ||||
|             logger.info('Some blocks are not indexed, skipping missing blocks during chain validation'); | ||||
|             partialMsg = true; | ||||
|           } | ||||
|           ++idx; | ||||
|           continue; | ||||
|         } | ||||
| 
 | ||||
|         if (blocks[idx].previous_block_hash !== blocks[idx - 1].hash) { | ||||
|           logger.warn(`Chain divergence detected at block ${blocks[idx - 1].height}, re-indexing newer blocks and hashrates`); | ||||
|           await this.$deleteBlocksFrom(blocks[idx - 1].height); | ||||
|           await HashratesRepository.$deleteHashratesFromTimestamp(blocks[idx - 1].timestamp - 604800); | ||||
|           return false; | ||||
|         } | ||||
|         ++currentHeight; | ||||
|         ++idx; | ||||
|       } | ||||
| 
 | ||||
|       logger.info(`${currentHeight} blocks hash validated in ${new Date().getTime() - start} ms`); | ||||
|       logger.info(`${idx} blocks hash validated in ${new Date().getTime() - start} ms`); | ||||
|       return true; | ||||
|     } catch (e) { | ||||
|       logger.err('Cannot validate chain of block hash. Reason: ' + (e instanceof Error ? e.message : e)); | ||||
| @ -457,7 +496,7 @@ class BlocksRepository { | ||||
|   /** | ||||
|    * Get the historical averaged block rewards | ||||
|    */ | ||||
|    public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> { | ||||
|   public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> { | ||||
|     try { | ||||
|       let query = `SELECT
 | ||||
|         CAST(AVG(height) as INT) as avg_height, | ||||
|  | ||||
| @ -702,8 +702,8 @@ class Routes { | ||||
| 
 | ||||
|   public async getBlock(req: Request, res: Response) { | ||||
|     try { | ||||
|       const result = await bitcoinApi.$getBlock(req.params.hash); | ||||
|       res.json(result); | ||||
|       const block = await blocks.$getBlock(req.params.hash); | ||||
|       res.json(block); | ||||
|     } catch (e) { | ||||
|       res.status(500).send(e instanceof Error ? e.message : e); | ||||
|     } | ||||
| @ -727,7 +727,7 @@ class Routes { | ||||
|       res.status(500).send(e instanceof Error ? e.message : e); | ||||
|     } | ||||
|   } | ||||
|    | ||||
| 
 | ||||
|   public async getBlocks(req: Request, res: Response) { | ||||
|     try { | ||||
|       loadingIndicators.setProgress('blocks', 0); | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { BlockExtended } from "../mempool.interfaces"; | ||||
| import { BlockExtended } from '../mempool.interfaces'; | ||||
| 
 | ||||
| export function prepareBlock(block: any): BlockExtended { | ||||
|   return <BlockExtended>{ | ||||
| @ -17,9 +17,11 @@ export function prepareBlock(block: any): BlockExtended { | ||||
|     extras: { | ||||
|       coinbaseRaw: block.coinbase_raw ?? block.extras.coinbaseRaw, | ||||
|       medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, | ||||
|       feeRange: block.feeRange ?? block.fee_range ?? block?.extras?.feeSpan, | ||||
|       feeRange: block.feeRange ?? block.fee_span, | ||||
|       reward: block.reward ?? block?.extras?.reward, | ||||
|       totalFees: block.totalFees ?? block?.fees ?? block?.extras.totalFees, | ||||
|       totalFees: block.totalFees ?? block?.fees ?? block?.extras?.totalFees, | ||||
|       avgFee: block?.extras?.avgFee ?? block.avg_fee, | ||||
|       avgFeeRate: block?.avgFeeRate ?? block.avg_fee_rate, | ||||
|       pool: block?.extras?.pool ?? (block?.pool_id ? { | ||||
|         id: block.pool_id, | ||||
|         name: block.pool_name, | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| FROM node:16.10.0-buster-slim AS builder | ||||
| FROM node:16.15.0-buster-slim AS builder | ||||
| 
 | ||||
| ARG commitHash | ||||
| ENV DOCKER_COMMIT_HASH=${commitHash} | ||||
| @ -11,7 +11,7 @@ RUN apt-get install -y build-essential python3 pkg-config | ||||
| RUN npm install | ||||
| RUN npm run build | ||||
| 
 | ||||
| FROM node:16.10.0-buster-slim | ||||
| FROM node:16.15.0-buster-slim | ||||
| 
 | ||||
| WORKDIR /backend | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| FROM node:16.10.0-buster-slim AS builder | ||||
| FROM node:16.15.0-buster-slim AS builder | ||||
| 
 | ||||
| ARG commitHash | ||||
| ENV DOCKER_COMMIT_HASH=${commitHash} | ||||
|  | ||||
| @ -40,7 +40,6 @@ import { AssetComponent } from './components/asset/asset.component'; | ||||
| import { AssetsComponent } from './components/assets/assets.component'; | ||||
| import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component'; | ||||
| import { StatusViewComponent } from './components/status-view/status-view.component'; | ||||
| import { MinerComponent } from './components/miner/miner.component'; | ||||
| import { SharedModule } from './shared/shared.module'; | ||||
| import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; | ||||
| import { FeesBoxComponent } from './components/fees-box/fees-box.component'; | ||||
| @ -109,7 +108,6 @@ import { BlockSizesWeightsGraphComponent } from './components/block-sizes-weight | ||||
|     LbtcPegsGraphComponent, | ||||
|     AssetComponent, | ||||
|     AssetsComponent, | ||||
|     MinerComponent, | ||||
|     StatusViewComponent, | ||||
|     FeesBoxComponent, | ||||
|     DashboardComponent, | ||||
|  | ||||
| @ -20,7 +20,7 @@ | ||||
|                 <td><a [routerLink]="['/block/' | relativeUrl, block.hash]" title="{{ block.hash }}">{{ block.hash | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="block.hash"></app-clipboard></td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td> | ||||
|                 <td i18n="block.timestamp">Timestamp</td> | ||||
|                 <td> | ||||
|                   ‎{{ block.time | date:'yyyy-MM-dd HH:mm' }} | ||||
|                   <div class="lg-inline"> | ||||
| @ -83,7 +83,7 @@ | ||||
|                 <td><span class="skeleton-loader"></span></td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td> | ||||
|                 <td i18n="block.timestamp">Timestamp</td> | ||||
|                 <td><span class="skeleton-loader"></span></td> | ||||
|               </tr> | ||||
|           </table> | ||||
|  | ||||
| @ -89,7 +89,7 @@ | ||||
|             </table> | ||||
|           </div> | ||||
| 
 | ||||
|           <div class="text-center"><a href="" [routerLink]="['/markets' | relativeUrl]" i18n="dashboard.view-all">View all »</a></div> | ||||
|           <div class="text-center"><a href="" [routerLink]="['/markets' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| @ -98,7 +98,7 @@ | ||||
|         <div class="card-body"> | ||||
|           <h5 class="card-title text-center" i18n="Latest Trades header">Latest Trades</h5> | ||||
|           <app-bisq-trades [trades$]="trades$" view="small"></app-bisq-trades> | ||||
|           <div class="text-center"><a href="" [routerLink]="['/markets' | relativeUrl]" i18n="dashboard.view-all">View all »</a></div> | ||||
|           <div class="text-center"><a href="" [routerLink]="['/markets' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
| @ -31,7 +31,7 @@ | ||||
|             <table class="table table-borderless table-striped"> | ||||
|               <tbody> | ||||
|                 <tr> | ||||
|                   <td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td> | ||||
|                   <td i18n="block.timestamp">Timestamp</td> | ||||
|                   <td> | ||||
|                     ‎{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }} | ||||
|                     <div class="lg-inline"> | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
|       <th style="width: 20%;" i18n>TXID</th> | ||||
|       <th class="d-none d-md-block" style="width: 100%;" i18n>Type</th> | ||||
|       <th style="width: 20%;" i18n>Amount</th> | ||||
|       <th style="width: 20%;" i18n>Confirmed</th> | ||||
|       <th style="width: 20%;" i18n="transaction.confirmed|Transaction Confirmed state">Confirmed</th> | ||||
|       <th class="d-none d-md-block" i18n>Height</th> | ||||
|     </thead> | ||||
|     <tbody *ngIf="transactions.value; else loadingTmpl"> | ||||
|  | ||||
| @ -118,7 +118,7 @@ export class AddressLabelsComponent implements OnInit { | ||||
|     } | ||||
|     const m = parseInt(opM.match(/[0-9]+/)[0], 10); | ||||
| 
 | ||||
|     this.label = `multisig ${m} of ${n}`; | ||||
|     this.label = $localize`:@@address-label.multisig:Multisig ${m}:multisigM: of ${n}:multisigN:` | ||||
|   } | ||||
| 
 | ||||
|   handleVout() { | ||||
|  | ||||
| @ -1,11 +1,13 @@ | ||||
| <div *ngIf="group$ | async as group; else loading"> | ||||
| <div *ngIf="group$ | async as group; else loading;"> | ||||
|    | ||||
|   <div class="main-title"> | ||||
|     <h2>{{ group.group.name }}</h2> | ||||
| 
 | ||||
|     <div class="sub-title" i18n>Group of {{ group.group.assets.length | number }} assets</div> | ||||
|   </div> | ||||
|   <ng-container *ngTemplateOutlet="title; context: group.group"></ng-container> | ||||
| 
 | ||||
|   <ng-template #title> | ||||
|     <div class="main-title"> | ||||
|       <h2>{{ group.name }}</h2> | ||||
|       <div class="sub-title" i18n>Group of {{ group.assets.length | number }} assets</div> | ||||
|     </div> | ||||
|   </ng-template> | ||||
| 
 | ||||
|   <div class="clearfix"></div> | ||||
| 
 | ||||
|  | ||||
| @ -2,11 +2,10 @@ | ||||
| 
 | ||||
| <div class="full-container"> | ||||
|   <div class="card-header mb-0 mb-md-4"> | ||||
|     <span i18n="mining.block-fee-rates">Block fee rates</span> | ||||
|     <span i18n="mining.block-fee-rates">Block Fee Rates</span> | ||||
|     <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|       <fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon> | ||||
|     </button> | ||||
| 
 | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
|         <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.availableTimespanDay >= 1"> | ||||
|  | ||||
| @ -62,7 +62,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.seoService.setTitle($localize`:@@mining.block-fee-rates:Block Fee Rates`); | ||||
|     this.seoService.setTitle($localize`:@@ed8e33059967f554ff06b4f5b6049c465b92d9b3:Block Fee Rates`); | ||||
|     this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); | ||||
|     this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); | ||||
|  | ||||
| @ -2,11 +2,10 @@ | ||||
| 
 | ||||
| <div class="full-container"> | ||||
|   <div class="card-header mb-0 mb-md-4"> | ||||
|     <span i18n="mining.block-fees">Block fees</span> | ||||
|     <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|     <span i18n="mining.block-fees">Block Fees</span> | ||||
|       <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|       <fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon> | ||||
|     </button> | ||||
| 
 | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
|         <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 144"> | ||||
|  | ||||
| @ -55,7 +55,7 @@ export class BlockFeesGraphComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.seoService.setTitle($localize`:@@mining.block-fees:Block Fees`); | ||||
|     this.seoService.setTitle($localize`:@@6c453b11fd7bd159ae30bc381f367bc736d86909:Block Fees`); | ||||
|     this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); | ||||
|     this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); | ||||
|  | ||||
| @ -3,11 +3,10 @@ | ||||
| <div class="full-container"> | ||||
| 
 | ||||
|   <div class="card-header mb-0 mb-md-4"> | ||||
|     <span i18n="mining.block-rewards">Block rewards</span> | ||||
|   <span i18n="mining.block-rewards">Block Rewards</span> | ||||
|     <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|       <fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon> | ||||
|     </button> | ||||
| 
 | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
|         <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 144"> | ||||
|  | ||||
| @ -53,7 +53,7 @@ export class BlockRewardsGraphComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.seoService.setTitle($localize`:@@mining.block-reward:Block Reward`); | ||||
|     this.seoService.setTitle($localize`:@@8ba8fe810458280a83df7fdf4c614dfc1a826445:Block Rewards`); | ||||
|     this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); | ||||
|     this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <div class="full-container"> | ||||
| 
 | ||||
|   <div class="card-header mb-0 mb-md-4"> | ||||
|     <span i18n="mining.block-size-weight">Block Sizes and Weights</span> | ||||
|     <span i18n="mining.block-sizes-weights">Block Sizes and Weights</span> | ||||
|     <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|       <fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon> | ||||
|     </button> | ||||
|  | ||||
| @ -62,7 +62,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit { | ||||
|   ngOnInit(): void { | ||||
|     let firstRun = true; | ||||
| 
 | ||||
|     this.seoService.setTitle($localize`:@@mining.hashrate-difficulty:Hashrate and Weight`); | ||||
|     this.seoService.setTitle($localize`:@@56fa1cd221491b6478998679cba2dc8d55ba330d:Block Sizes and Weights`); | ||||
|     this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); | ||||
|     this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); | ||||
|  | ||||
| @ -81,15 +81,26 @@ | ||||
|               <ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees"> | ||||
|                 <tr> | ||||
|                   <td i18n="block.total-fees|Total fees in a block">Total fees</td> | ||||
|                   <td *ngIf="network !== 'liquid' && network !== 'liquidtestnet'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat></span></td> | ||||
|                   <td *ngIf="network !== 'liquid' && network !== 'liquidtestnet'; else liquidTotalFees"> | ||||
|                     <app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount> | ||||
|                     <span class="fiat"> | ||||
|                       <app-fiat [value]="block.extras.totalFees" digitsInfo="1.0-0"></app-fiat> | ||||
|                     </span> | ||||
|                   </td> | ||||
|                   <ng-template #liquidTotalFees> | ||||
|                     <td><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount>  <app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat></td> | ||||
|                     <td> | ||||
|                       <app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount>  <app-fiat | ||||
|                         [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat> | ||||
|                     </td> | ||||
|                   </ng-template> | ||||
|                 </tr> | ||||
|                 <tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'"> | ||||
|                   <td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td> | ||||
|                   <td> | ||||
|                     <app-amount [satoshis]="(blockSubsidy + fees) * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat></span> | ||||
|                     <app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-3" [noFiat]="true"></app-amount> | ||||
|                     <span class="fiat"> | ||||
|                       <app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat> | ||||
|                     </span> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               </ng-template> | ||||
| @ -105,7 +116,18 @@ | ||||
|               </ng-template> | ||||
|               <tr> | ||||
|                 <td i18n="block.miner">Miner</td> | ||||
|                 <td><app-miner [coinbaseTransaction]="coinbaseTx"></app-miner></td> | ||||
|                 <td *ngIf="stateService.env.MINING_DASHBOARD"> | ||||
|                   <a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]" class="badge" | ||||
|                     [class]="block.extras.pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'"> | ||||
|                     {{ block.extras.pool.name }} | ||||
|                   </a> | ||||
|                 </td> | ||||
|                 <td *ngIf="!stateService.env.MINING_DASHBOARD && stateService.env.BASE_MODULE === 'mempool'"> | ||||
|                   <span placement="bottom" class="badge" | ||||
|                     [class]="block.extras.pool.name === 'Unknown' ? 'badge-secondary' : 'badge-primary'"> | ||||
|                     {{ block.extras.pool.name }} | ||||
|                 </span> | ||||
|                 </td> | ||||
|               </tr> | ||||
|             </tbody> | ||||
|           </table> | ||||
|  | ||||
| @ -10,6 +10,7 @@ import { SeoService } from 'src/app/services/seo.service'; | ||||
| import { WebsocketService } from 'src/app/services/websocket.service'; | ||||
| import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe'; | ||||
| import { BlockExtended } from 'src/app/interfaces/node-api.interface'; | ||||
| import { ApiService } from 'src/app/services/api.service'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-block', | ||||
| @ -31,7 +32,6 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|   blockSubsidy: number; | ||||
|   fees: number; | ||||
|   paginationMaxSize: number; | ||||
|   coinbaseTx: Transaction; | ||||
|   page = 1; | ||||
|   itemsPerPage: number; | ||||
|   txsLoadingStatus$: Observable<number>; | ||||
| @ -50,10 +50,11 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|     private location: Location, | ||||
|     private router: Router, | ||||
|     private electrsApiService: ElectrsApiService, | ||||
|     private stateService: StateService, | ||||
|     public stateService: StateService, | ||||
|     private seoService: SeoService, | ||||
|     private websocketService: WebsocketService, | ||||
|     private relativeUrlPipe: RelativeUrlPipe, | ||||
|     private apiService: ApiService | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
| @ -88,7 +89,6 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|         const blockHash: string = params.get('id') || ''; | ||||
|         this.block = undefined; | ||||
|         this.page = 1; | ||||
|         this.coinbaseTx = undefined; | ||||
|         this.error = undefined; | ||||
|         this.fees = undefined; | ||||
|         this.stateService.markBlock$.next({}); | ||||
| @ -124,7 +124,7 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|                   this.location.replaceState( | ||||
|                     this.router.createUrlTree([(this.network ? '/' + this.network : '') + '/block/', hash]).toString() | ||||
|                   ); | ||||
|                   return this.electrsApiService.getBlock$(hash); | ||||
|                   return this.apiService.getBlock$(hash); | ||||
|                 }) | ||||
|               ); | ||||
|           } | ||||
| @ -134,7 +134,7 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|             return of(blockInCache); | ||||
|           } | ||||
| 
 | ||||
|           return this.electrsApiService.getBlock$(blockHash); | ||||
|           return this.apiService.getBlock$(blockHash); | ||||
|         } | ||||
|       }), | ||||
|       tap((block: BlockExtended) => { | ||||
| @ -145,7 +145,6 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|         this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`); | ||||
|         this.isLoadingBlock = false; | ||||
|         this.coinbaseTx = block?.extras?.coinbaseTx; | ||||
|         this.setBlockSubsidy(); | ||||
|         if (block?.extras?.reward !== undefined) { | ||||
|           this.fees = block.extras.reward / 100000000 - this.blockSubsidy; | ||||
| @ -167,9 +166,6 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|       if (this.fees === undefined && transactions[0]) { | ||||
|         this.fees = transactions[0].vout.reduce((acc: number, curr: Vout) => acc + curr.value, 0) / 100000000 - this.blockSubsidy; | ||||
|       } | ||||
|       if (!this.coinbaseTx && transactions[0]) { | ||||
|         this.coinbaseTx = transactions[0]; | ||||
|       } | ||||
|       this.transactions = transactions; | ||||
|       this.isLoadingTransactions = false; | ||||
|     }, | ||||
| @ -212,13 +208,10 @@ export class BlockComponent implements OnInit, OnDestroy { | ||||
|     this.queryParamsSubscription.unsubscribe(); | ||||
|   } | ||||
| 
 | ||||
|   // TODO - Refactor this.fees/this.reward for liquid because it is not
 | ||||
|   // used anymore on Bitcoin networks (we use block.extras directly)
 | ||||
|   setBlockSubsidy() { | ||||
|     if (this.network === 'liquid' || this.network === 'liquidtestnet') { | ||||
|       this.blockSubsidy = 0; | ||||
|       return; | ||||
|     } | ||||
|     const halvings = Math.floor(this.block.height / 210000); | ||||
|     this.blockSubsidy = 50 * 2 ** -halvings; | ||||
|     this.blockSubsidy = 0; | ||||
|   } | ||||
| 
 | ||||
|   pageChange(page: number, target: HTMLElement) { | ||||
|  | ||||
| @ -1,27 +1,25 @@ | ||||
| <app-indexing-progress *ngIf="!widget"></app-indexing-progress> | ||||
| 
 | ||||
| <div class="container-xl" [class]="widget ? 'widget' : 'full-height'"> | ||||
|   <h1 *ngIf="!widget" class="float-left" i18n="latest-blocks.blocks">Blocks</h1> | ||||
|   <h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1> | ||||
| 
 | ||||
|   <div class="clearfix"></div> | ||||
| 
 | ||||
|   <div style="min-height: 295px"> | ||||
|     <table class="table table-borderless"> | ||||
|       <thead> | ||||
|         <th class="height" [class]="widget ? 'widget' : ''" i18n="latest-blocks.height">Height</th> | ||||
|         <th class="pool text-left" [class]="widget ? 'widget' : ''" i18n="mining.pool-name"> | ||||
|           Pool</th> | ||||
|         <th class="height text-left" [class]="widget ? 'widget' : ''" i18n="latest-blocks.height">Height</th> | ||||
|         <th class="pool text-left" [class]="widget ? 'widget' : ''" i18n="mining.pool-name">Pool</th> | ||||
|         <th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget">Timestamp</th> | ||||
|         <th class="mined" i18n="latest-blocks.mined" *ngIf="!widget">Mined</th> | ||||
|         <th class="reward text-right" i18n="latest-blocks.reward" [class]="widget ? 'widget' : ''"> | ||||
|           Reward</th> | ||||
|         <th class="reward text-right" i18n="latest-blocks.reward" [class]="widget ? 'widget' : ''">Reward</th> | ||||
|         <th class="fees text-right" i18n="latest-blocks.fees" *ngIf="!widget">Fees</th> | ||||
|         <th class="txs text-right" i18n="latest-blocks.transactions" [class]="widget ? 'widget' : ''">Txs</th> | ||||
|         <th class="txs text-right" i18n="dashboard.txs" [class]="widget ? 'widget' : ''">TXs</th> | ||||
|         <th class="size" i18n="latest-blocks.size" *ngIf="!widget">Size</th> | ||||
|       </thead> | ||||
|       <tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''"> | ||||
|         <tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock"> | ||||
|           <td [class]="widget ? 'widget' : ''"> | ||||
|           <td class="text-left" [class]="widget ? 'widget' : ''"> | ||||
|             <a [routerLink]="['/block' | relativeUrl, block.height]">{{ block.height | ||||
|               }}</a> | ||||
|           </td> | ||||
| @ -29,7 +27,7 @@ | ||||
|             <div class="tooltip-custom"> | ||||
|               <a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]"> | ||||
|                 <img width="22" height="22" src="{{ block.extras.pool['logo'] }}" | ||||
|                   onError="this.src = './resources/mining-pools/default.svg'"> | ||||
|                   onError="this.src = './resources/mining-pools/default.svg'" [alt]="'Logo of ' + block.extras.pool.name + ' mining pool'"> | ||||
|                 <span class="pool-name">{{ block.extras.pool.name }}</span> | ||||
|               </a> | ||||
|               <span *ngIf="!widget" class="tooltiptext badge badge-secondary scriptmessage">{{ block.extras.coinbaseRaw | hex2ascii }}</span> | ||||
| @ -42,10 +40,10 @@ | ||||
|             <app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> | ||||
|           </td> | ||||
|           <td class="reward text-right" [class]="widget ? 'widget' : ''"> | ||||
|             <app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-2"></app-amount> | ||||
|             <app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount> | ||||
|           </td> | ||||
|           <td class="fees text-right" *ngIf="!widget"> | ||||
|             <app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-2"></app-amount> | ||||
|             <app-amount [satoshis]="block.extras.totalFees" [noFiat]="true" digitsInfo="1.2-2"></app-amount> | ||||
|           </td> | ||||
|           <td class="txs text-right" [class]="widget ? 'widget' : ''"> | ||||
|             {{ block.tx_count | number }} | ||||
| @ -62,7 +60,7 @@ | ||||
|       <ng-template #skeleton> | ||||
|         <tbody> | ||||
|           <tr *ngFor="let item of skeletonLines"> | ||||
|             <td class="height" [class]="widget ? 'widget' : ''"> | ||||
|             <td class="height text-left" [class]="widget ? 'widget' : ''"> | ||||
|               <span class="skeleton-loader" style="max-width: 75px"></span> | ||||
|             </td> | ||||
|             <td class="pool text-left" [class]="widget ? 'widget' : ''"> | ||||
|  | ||||
| @ -11,13 +11,10 @@ | ||||
|   max-width: 100%; | ||||
| } | ||||
| 
 | ||||
| td { | ||||
|   padding-top: 0.7rem !important; | ||||
| tr, td, th { | ||||
|   border: 0px; | ||||
|   padding-top: 0.65rem !important; | ||||
|   padding-bottom: 0.7rem !important; | ||||
|   @media (max-width: 376px) { | ||||
|     padding-top: 0.73rem !important; | ||||
|     padding-bottom: 0.73rem !important; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .clear-link { | ||||
| @ -41,7 +38,7 @@ td { | ||||
| } | ||||
| .pool.widget { | ||||
|   width: 40%; | ||||
|   padding-left: 30px; | ||||
|   padding-left: 24px; | ||||
|   @media (max-width: 376px) { | ||||
|     width: 60%; | ||||
|   } | ||||
| @ -56,7 +53,7 @@ td { | ||||
|   width: 10%; | ||||
| } | ||||
| .height.widget { | ||||
|   width: 20%; | ||||
|   width: 15%; | ||||
|   @media (max-width: 576px) { | ||||
|     width: 10%; | ||||
|   } | ||||
|  | ||||
| @ -39,7 +39,7 @@ export class BlocksList implements OnInit { | ||||
|       this.websocketService.want(['blocks']); | ||||
|     } | ||||
| 
 | ||||
|     this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()]; | ||||
|     this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; | ||||
|     this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; | ||||
| 
 | ||||
|     this.blocks$ = combineLatest([ | ||||
| @ -61,7 +61,7 @@ export class BlocksList implements OnInit { | ||||
|                     block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; | ||||
|                 } | ||||
|                 if (this.widget) { | ||||
|                   return blocks.slice(0, 5); | ||||
|                   return blocks.slice(0, 6); | ||||
|                 } | ||||
|                 return blocks; | ||||
|               }), | ||||
| @ -85,7 +85,7 @@ export class BlocksList implements OnInit { | ||||
|           blocks[1][0].extras.pool.logo = `./resources/mining-pools/` + | ||||
|             blocks[1][0].extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; | ||||
|           acc.unshift(blocks[1][0]); | ||||
|           acc = acc.slice(0, this.widget ? 5 : 15); | ||||
|           acc = acc.slice(0, this.widget ? 6 : 15); | ||||
|           return acc; | ||||
|         }, []) | ||||
|       ); | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
|       </tr> | ||||
|     </tbody> | ||||
|     <tbody *ngIf="isLoading"> | ||||
|       <tr *ngFor="let item of [1,2,3,4,5]"> | ||||
|       <tr *ngFor="let item of [1,2,3,4,5,6]"> | ||||
|         <td class="d-none d-md-block w-75"><span class="skeleton-loader"></span></td> | ||||
|         <td class="text-left"><span class="skeleton-loader w-75"></span></td> | ||||
|         <td class="text-right"><span class="skeleton-loader w-75"></span></td> | ||||
|  | ||||
| @ -2,10 +2,15 @@ | ||||
|   width: 100%; | ||||
|   text-align: left; | ||||
|   table-layout:fixed; | ||||
|   tr, td, th { | ||||
|   tr, th { | ||||
|     border: 0px; | ||||
|     padding-top: 0.65rem !important; | ||||
|     padding-bottom: 0.7rem !important; | ||||
|   } | ||||
|   td { | ||||
|     border: 0px; | ||||
|     padding-top: 0.71rem !important; | ||||
|     padding-bottom: 0.75rem !important; | ||||
|     width: 25%; | ||||
|     @media (max-width: 376px) { | ||||
|       padding: 0.85rem; | ||||
|  | ||||
| @ -54,7 +54,7 @@ export class DifficultyAdjustmentsTable implements OnInit { | ||||
| 
 | ||||
|           return { | ||||
|             availableTimespanDay: availableTimespanDay, | ||||
|             difficulty: tableData.slice(0, 5), | ||||
|             difficulty: tableData.slice(0, 6), | ||||
|           }; | ||||
|         }), | ||||
|       ); | ||||
|  | ||||
| @ -5,33 +5,19 @@ | ||||
|     <button class="btn btn-primary w-100" id="dropdownBasic1" ngbDropdownToggle i18n="mining">Mining</button> | ||||
|     <div ngbDropdownMenu aria-labelledby="dropdownBasic1"> | ||||
|       <a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/mining/pools' | relativeUrl]" | ||||
|         i18n="mining.pools"> | ||||
|         Pools ranking | ||||
|       </a> | ||||
|         i18n="mining.pools">Pools Ranking</a> | ||||
|       <a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/mining/pools-dominance' | relativeUrl]" | ||||
|         i18n="mining.pools-dominance"> | ||||
|         Pools dominance | ||||
|       </a> | ||||
|         i18n="mining.pools-dominance">Pools Dominance</a> | ||||
|       <a class="dropdown-item" routerLinkActive="active" | ||||
|         [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="mining.hashrate-difficulty"> | ||||
|         Hashrate & Difficulty | ||||
|       </a> | ||||
|         [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="mining.hashrate-difficulty">Hashrate & Difficulty</a> | ||||
|       <a class="dropdown-item" routerLinkActive="active" | ||||
|         [routerLink]="['/graphs/mining/block-fee-rates' | relativeUrl]" i18n="mining.block-fee-rates"> | ||||
|         Block Fee Rates | ||||
|       </a> | ||||
|         [routerLink]="['/graphs/mining/block-fee-rates' | relativeUrl]" i18n="mining.block-fee-rates">Block Fee Rates</a> | ||||
|       <a class="dropdown-item" routerLinkActive="active" | ||||
|         [routerLink]="['/graphs/mining/block-fees' | relativeUrl]" i18n="mining.block-fees"> | ||||
|         Block Fees | ||||
|       </a> | ||||
|         [routerLink]="['/graphs/mining/block-fees' | relativeUrl]" i18n="mining.block-fees">Block Fees</a> | ||||
|       <a class="dropdown-item" routerLinkActive="active" | ||||
|         [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]" i18n="mining.block-rewards"> | ||||
|         Block Rewards | ||||
|       </a> | ||||
|         [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]" i18n="mining.block-rewards">Block Rewards</a> | ||||
|       <a class="dropdown-item" routerLinkActive="active" | ||||
|         [routerLink]="['/graphs/mining/block-sizes-weights' | relativeUrl]" i18n="mining.block-sizes-weights"> | ||||
|         Block Sizes and Weights | ||||
|       </a> | ||||
|         [routerLink]="['/graphs/mining/block-sizes-weights' | relativeUrl]" i18n="mining.block-sizes-weights">Block Sizes and Weights</a> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| @ -52,7 +52,7 @@ | ||||
| .chart-widget { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   max-height: 270px; | ||||
|   height: 240px; | ||||
| } | ||||
| 
 | ||||
| .formRadioGroup { | ||||
|  | ||||
| @ -64,7 +64,7 @@ export class HashrateChartComponent implements OnInit { | ||||
|     if (this.widget) { | ||||
|       this.miningWindowPreference = '1y'; | ||||
|     } else { | ||||
|       this.seoService.setTitle($localize`:@@mining.hashrate-difficulty:Hashrate and Difficulty`); | ||||
|       this.seoService.setTitle($localize`:@@3510fc6daa1d975f331e3a717bdf1a34efa06dff:Hashrate & Difficulty`); | ||||
|       this.miningWindowPreference = this.miningService.getDefaultTimespan('1m'); | ||||
|     } | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); | ||||
|  | ||||
| @ -3,11 +3,11 @@ | ||||
| <div class="full-container"> | ||||
| 
 | ||||
|   <div class="card-header  mb-0 mb-md-4"> | ||||
|     <span i18n="mining.pools-dominance">Mining pools dominance</span> | ||||
|     <span i18n="mining.pools-dominance">Pools Dominance</span> | ||||
|     <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|       <fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon> | ||||
|     </button> | ||||
| 
 | ||||
|     <span i18n="mining.pools-dominance">Pools Dominance</span> | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(hashrateObservable$ | async) as stats"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
|         <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320"> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <div class="container-xl"> | ||||
|   <h1 class="float-left" i18n="latest-blocks.blocks">Blocks</h1> | ||||
|   <h1 class="float-left" i18n="master-page.blocks">Blocks</h1> | ||||
|   <br> | ||||
| 
 | ||||
|   <div class="clearfix"></div> | ||||
|  | ||||
| @ -36,7 +36,7 @@ export class LatestBlocksComponent implements OnInit, OnDestroy { | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|     this.seoService.setTitle($localize`:@@f4cba7faeb126346f09cc6af30124f9a343f7a28:Blocks`); | ||||
|     this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`); | ||||
|     this.websocketService.want(['blocks']); | ||||
| 
 | ||||
|     this.network$ = merge(of(''), this.stateService.networkChanged$); | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
|   <nav class="navbar navbar-expand-md navbar-dark bg-dark"> | ||||
|   <a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;"> | ||||
|     <ng-container *ngIf="{ val: connectionState$ | async } as connectionState"> | ||||
|       <img [src]="officialMempoolSpace ? './resources/mempool-space-logo.png' : './resources/mempool-logo.png'" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }"> | ||||
|       <img [src]="officialMempoolSpace ? './resources/mempool-space-logo.png' : './resources/mempool-logo.png'" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }" alt="The Mempool Open Source Project logo"> | ||||
|       <div class="connection-badge"> | ||||
|         <div class="badge badge-warning" *ngIf="connectionState.val === 0" i18n="master-page.offline">Offline</div> | ||||
|         <div class="badge badge-warning" *ngIf="connectionState.val === 1" i18n="master-page.reconnecting">Reconnecting...</div> | ||||
| @ -13,16 +13,16 @@ | ||||
| 
 | ||||
|   <div (window:resize)="onResize($event)" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_ENABLED"> | ||||
|     <button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true"> | ||||
|       <img src="./resources/{{ network.val === '' ? 'bitcoin' : network.val }}-logo.png" style="width: 25px; height: 25px;" class="mr-1"> | ||||
|       <img src="./resources/{{ network.val === '' ? 'bitcoin' : network.val }}-logo.png" style="width: 25px; height: 25px;" class="mr-1" [alt]="(network.val === '' ? 'bitcoin' : network.val) + ' logo'"> | ||||
|     </button> | ||||
|     <div ngbDropdownMenu [ngClass]="{'dropdown-menu-right' : isMobile}"> | ||||
|       <button ngbDropdownItem class="mainnet" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</button> | ||||
|       <button ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet" [class.active]="network.val === 'signet'" routerLink="/signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1"> Signet</button> | ||||
|       <button ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</button> | ||||
|       <button ngbDropdownItem class="mainnet" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1" alt="bitcoin logo"> Mainnet</button> | ||||
|       <button ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet" [class.active]="network.val === 'signet'" routerLink="/signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1" alt="signet logo"> Signet</button> | ||||
|       <button ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1" alt="testnet logo"> Testnet</button> | ||||
|       <h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6> | ||||
|       <a [href]="env.BISQ_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.BISQ_ENABLED" class="bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a> | ||||
|       <a [href]="env.LIQUID_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid" [class.active]="network.val === 'liquid'"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a> | ||||
|       <a [href]="env.LIQUID_WEBSITE_URL + urlLanguage + '/testnet'" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquid'"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</a> | ||||
|       <a [href]="env.BISQ_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.BISQ_ENABLED" class="bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1" alt="bisq logo"> Bisq</a> | ||||
|       <a [href]="env.LIQUID_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid" [class.active]="network.val === 'liquid'"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1" alt="liquid mainnet logo"> Liquid</a> | ||||
|       <a [href]="env.LIQUID_WEBSITE_URL + urlLanguage + '/testnet'" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquid'"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1" alt="liquid testnet logo"> Liquid Testnet</a> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
| @ -32,7 +32,7 @@ | ||||
|         <a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a> | ||||
|       </li> | ||||
|       <li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD"> | ||||
|         <a class="nav-link" [routerLink]="['/mining' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="master-page.mining-dashboard" title="Mining Dashboard"></fa-icon></a> | ||||
|         <a class="nav-link" [routerLink]="['/mining' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="mining.mining-dashboard" title="Mining Dashboard"></fa-icon></a> | ||||
|       </li> | ||||
|       <li class="nav-item" routerLinkActive="active" id="btn-blocks" *ngIf="!stateService.env.MINING_DASHBOARD"> | ||||
|         <a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a> | ||||
|  | ||||
| @ -21,7 +21,7 @@ | ||||
|               <td><span class="yellow-color">{{ mempoolBlock.feeRange[0] | number:'1.0-0' }} - {{ mempoolBlock.feeRange[mempoolBlock.feeRange.length - 1] | number:'1.0-0' }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></span></td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|               <td i18n="mempool-block.total-fees">Total fees</td> | ||||
|               <td i18n="block.total-fees|Total fees in a block">Total fees</td> | ||||
|               <td><app-amount [satoshis]="mempoolBlock.totalFees" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="mempoolBlock.totalFees" digitsInfo="1.0-0"></app-fiat></span></td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|  | ||||
| @ -68,7 +68,7 @@ export class MempoolBlockComponent implements OnInit, OnDestroy { | ||||
|   getOrdinal(mempoolBlock: MempoolBlock): string { | ||||
|     const blocksInBlock = Math.ceil(mempoolBlock.blockVSize / this.stateService.blockVSize); | ||||
|     if (this.mempoolBlockIndex === 0) { | ||||
|       return $localize`:@@mempool-block.next.block:Next block`; | ||||
|       return $localize`:@@bdf0e930eb22431140a2eaeacd809cc5f8ebd38c:Next Block`; | ||||
|     } else if (this.mempoolBlockIndex === this.stateService.env.KEEP_BLOCKS_AMOUNT - 1 && blocksInBlock > 1) { | ||||
|       return $localize`:@@mempool-block.stack.of.blocks:Stack of ${blocksInBlock}:INTERPOLATION: mempool blocks`; | ||||
|     } else { | ||||
|  | ||||
| @ -1,12 +0,0 @@ | ||||
| <ng-template [ngIf]="loading" [ngIfElse]="done"> | ||||
|   <span class="skeleton-loader"></span> | ||||
| </ng-template> | ||||
| 
 | ||||
| <ng-template #done> | ||||
|   <ng-template [ngIf]="miner" [ngIfElse]="unknownMiner"> | ||||
|     <a placement="bottom" [ngbTooltip]="title" [href]="url" [target]="target" class="badge badge-primary">{{ miner }}</a> | ||||
|   </ng-template> | ||||
|   <ng-template #unknownMiner> | ||||
|     <span class="badge badge-secondary" i18n="miner.tag.unknown-miner">Unknown</span> | ||||
|   </ng-template> | ||||
| </ng-template> | ||||
| @ -1,3 +0,0 @@ | ||||
| .badge { | ||||
|   font-size: 14px; | ||||
| } | ||||
| @ -1,94 +0,0 @@ | ||||
| import { Component, Input, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; | ||||
| import { AssetsService } from 'src/app/services/assets.service'; | ||||
| import { Transaction } from 'src/app/interfaces/electrs.interface'; | ||||
| import { StateService } from 'src/app/services/state.service'; | ||||
| import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-miner', | ||||
|   templateUrl: './miner.component.html', | ||||
|   styleUrls: ['./miner.component.scss'], | ||||
|   changeDetection: ChangeDetectionStrategy.OnPush, | ||||
| }) | ||||
| export class MinerComponent implements OnChanges { | ||||
|   @Input() coinbaseTransaction: Transaction; | ||||
|   miner = ''; | ||||
|   title = ''; | ||||
|   url = ''; | ||||
|   target = '_blank'; | ||||
|   loading = true; | ||||
| 
 | ||||
|   constructor( | ||||
|     private assetsService: AssetsService, | ||||
|     private cd: ChangeDetectorRef, | ||||
|     public stateService: StateService, | ||||
|     private relativeUrlPipe: RelativeUrlPipe, | ||||
|   ) { } | ||||
| 
 | ||||
|   ngOnChanges() { | ||||
|     this.miner = ''; | ||||
|     if (this.stateService.env.MINING_DASHBOARD) { | ||||
|       this.miner = 'Unknown'; | ||||
|       this.url = this.relativeUrlPipe.transform(`/mining/pool/unknown`); | ||||
|       this.target = ''; | ||||
|     } | ||||
|     this.loading = true; | ||||
|     this.findMinerFromCoinbase(); | ||||
|   } | ||||
| 
 | ||||
|   findMinerFromCoinbase() { | ||||
|     if (this.coinbaseTransaction == null || this.coinbaseTransaction.vin == null || this.coinbaseTransaction.vin.length === 0) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     this.assetsService.getMiningPools$.subscribe((pools) => { | ||||
|       for (const vout of this.coinbaseTransaction.vout) { | ||||
|         if (!vout.scriptpubkey_address) { | ||||
|           continue; | ||||
|         } | ||||
| 
 | ||||
|         if (pools.payout_addresses[vout.scriptpubkey_address]) { | ||||
|             this.miner = pools.payout_addresses[vout.scriptpubkey_address].name; | ||||
|             this.title = $localize`:@@miner-identified-by-payout:Identified by payout address: '${vout.scriptpubkey_address}:PAYOUT_ADDRESS:'`; | ||||
|             const pool = pools.payout_addresses[vout.scriptpubkey_address]; | ||||
|             if (this.stateService.env.MINING_DASHBOARD && pools.slugs && pools.slugs[pool.name] !== undefined) { | ||||
|               this.url = this.relativeUrlPipe.transform(`/mining/pool/${pools.slugs[pool.name]}`); | ||||
|               this.target = ''; | ||||
|             } else { | ||||
|               this.url = pool.link; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         for (const tag in pools.coinbase_tags) { | ||||
|           if (pools.coinbase_tags.hasOwnProperty(tag)) { | ||||
|             const coinbaseAscii = this.hex2ascii(this.coinbaseTransaction.vin[0].scriptsig); | ||||
|             if (coinbaseAscii.indexOf(tag) > -1) { | ||||
|               const pool = pools.coinbase_tags[tag]; | ||||
|               this.miner = pool.name; | ||||
|               this.title = $localize`:@@miner-identified-by-coinbase:Identified by coinbase tag: '${tag}:TAG:'`; | ||||
|               if (this.stateService.env.MINING_DASHBOARD && pools.slugs && pools.slugs[pool.name] !== undefined) { | ||||
|                 this.url = this.relativeUrlPipe.transform(`/mining/pool/${pools.slugs[pool.name]}`); | ||||
|                 this.target = ''; | ||||
|               } else { | ||||
|                 this.url = pool.link; | ||||
|               } | ||||
|               break; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       this.loading = false; | ||||
|       this.cd.markForCheck(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   hex2ascii(hex: string) { | ||||
|     let str = ''; | ||||
|     for (let i = 0; i < hex.length; i += 2) { | ||||
|       str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); | ||||
|     } | ||||
|     return str; | ||||
|   } | ||||
| } | ||||
| @ -11,7 +11,7 @@ | ||||
|         <span style="font-size: xx-small" i18n="mining.144-blocks">(144 blocks)</span> | ||||
|       </div> | ||||
|       <div class="card-wrapper"> | ||||
|         <div class="card" style="height: 123px"> | ||||
|         <div class="card"> | ||||
|           <div class="card-body more-padding"> | ||||
|             <app-reward-stats></app-reward-stats> | ||||
|           </div> | ||||
| @ -22,29 +22,25 @@ | ||||
|     <!-- difficulty adjustment --> | ||||
|     <div class="col"> | ||||
|       <div class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div> | ||||
|       <div class="card" style="height: 123px"> | ||||
|         <app-difficulty [showTitle]="false" [showProgress]="false" [showHalving]="true"></app-difficulty> | ||||
|       </div> | ||||
|       <app-difficulty [showTitle]="false" [showProgress]="false" [showHalving]="true"></app-difficulty> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- pool distribution --> | ||||
|     <div class="col"> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|     <div class="col" style="margin-bottom: 1.47rem"> | ||||
|       <div class="card graph-card"> | ||||
|         <div class="card-body pl-2 pr-2"> | ||||
|           <app-pool-ranking [widget]=true></app-pool-ranking> | ||||
|           <div class="mt-1"><a [routerLink]="['/graphs/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
|           <div class="mt-1"><a [routerLink]="['/graphs/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- hashrate --> | ||||
|     <div class="col"> | ||||
|     <div class="col" style="margin-bottom: 1.47rem"> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|           <app-hashrate-chart [widget]=true></app-hashrate-chart> | ||||
|           <div class="mt-1"><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
|         <div class="card-body pl-lg-3 pr-lg-3 pl-2 pr-2"> | ||||
|           <app-hashrate-chart [widget]="true"></app-hashrate-chart> | ||||
|           <div class="mt-1"><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| @ -53,12 +49,9 @@ | ||||
|     <div class="col"> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|           <h5 class="card-title"> | ||||
|             Latest blocks | ||||
|           </h5> | ||||
|           <h5 class="card-title" i18n="dashboard.latest-blocks">Latest blocks</h5> | ||||
|           <app-blocks-list [widget]=true></app-blocks-list> | ||||
|           <div><a [routerLink]="['/mining/blocks' | relativeUrl]" i18n="dashboard.view-more">View | ||||
|               more »</a></div> | ||||
|           <div><a [routerLink]="['/mining/blocks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| @ -67,12 +60,9 @@ | ||||
|     <div class="col"> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|           <h5 class="card-title"> | ||||
|             Adjustments | ||||
|           </h5> | ||||
|           <h5 class="card-title" i18n="dashboard.adjustments">Adjustments</h5> | ||||
|           <app-difficulty-adjustments-table></app-difficulty-adjustments-table> | ||||
|           <div><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
|           <div><a [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
| @ -14,6 +14,13 @@ | ||||
|   background-color: #1d1f31; | ||||
| } | ||||
| 
 | ||||
| .graph-card { | ||||
|   height: 100%; | ||||
|   @media (min-width: 992px) { | ||||
|     height: 385px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .card-title { | ||||
|   font-size: 1rem; | ||||
|   color: #4a68b9; | ||||
| @ -22,9 +29,6 @@ | ||||
|   color: #4a68b9; | ||||
| } | ||||
| 
 | ||||
| .card-body { | ||||
|   padding: 1.25rem 1rem 0.75rem 1rem; | ||||
| } | ||||
| .card-body.pool-ranking { | ||||
|   padding: 1.25rem 0.25rem 0.75rem 0.25rem; | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,7 @@ export class MiningDashboardComponent implements OnInit { | ||||
|     private seoService: SeoService, | ||||
|     private websocketService: WebsocketService, | ||||
|   ) { | ||||
|     this.seoService.setTitle($localize`:@@mining.mining-dashboard:Mining Dashboard`); | ||||
|     this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Mining Dashboard`); | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|   <div *ngIf="widget"> | ||||
|     <div class="pool-distribution" *ngIf="(miningStatsObservable$ | async) as miningStats; else loadingReward"> | ||||
|       <div class="item"> | ||||
|         <h5 class="card-title" i18n="mining.miners-luck">Pools luck (1w)</h5> | ||||
|         <h5 class="card-title" i18n="mining.miners-luck">Pools Luck (1w)</h5> | ||||
|         <p class="card-text"> | ||||
|           {{ miningStats['minersLuck'] }}% | ||||
|         </p> | ||||
| @ -17,7 +17,7 @@ | ||||
|         </p> | ||||
|       </div> | ||||
|       <div class="item"> | ||||
|         <h5 class="card-title" i18n="mining.miners-count">Pools count (1w)</h5> | ||||
|         <h5 class="card-title" i18n="mining.miners-count">Pools Count (1w)</h5> | ||||
|         <p class="card-text"> | ||||
|           {{ miningStats.pools.length }} | ||||
|         </p> | ||||
| @ -26,11 +26,10 @@ | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="card-header" *ngIf="!widget"> | ||||
|     <span i18n="mining.mining-pool-share">Mining pools share</span> | ||||
|     <span i18n="mining.pools">Pools Ranking</span> | ||||
|     <button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()"> | ||||
|       <fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon> | ||||
|     </button> | ||||
| 
 | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" | ||||
|       *ngIf="!widget && (miningStatsObservable$ | async) as stats"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
| @ -62,7 +61,7 @@ | ||||
|           <input ngbButton type="radio" [value]="'3y'" fragment="3y"> 3Y | ||||
|         </label> | ||||
|         <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.totalBlockCount > 157680"> | ||||
|           <input ngbButton type="radio" [value]="'all'" fragment="all"> ALL | ||||
|           <input ngbButton type="radio" [value]="'all'" fragment="all"><span i18n>All</span> | ||||
|         </label> | ||||
|       </div> | ||||
|     </form> | ||||
| @ -87,14 +86,15 @@ | ||||
|           <th class="" i18n="mining.pool-name">Pool</th> | ||||
|           <th class="" *ngIf="this.miningWindowPreference === '24h'" i18n="mining.hashrate">Hashrate</th> | ||||
|           <th class="" i18n="master-page.blocks">Blocks</th> | ||||
|           <th class="d-none d-md-block" i18n="mining.empty-blocks">Empty Blocks</th> | ||||
|           <th class="d-none d-md-block" i18n="mining.empty-blocks">Empty blocks</th> | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody *ngIf="(miningStatsObservable$ | async) as miningStats"> | ||||
|         <tr *ngFor="let pool of miningStats.pools"> | ||||
|           <td class="d-none d-md-block">{{ pool.rank }}</td> | ||||
|           <td class="text-right"><img width="25" height="25" src="{{ pool.logo }}" | ||||
|               onError="this.src = './resources/mining-pools/default.svg'"></td> | ||||
|           <td class="text-right"> | ||||
|             <img width="25" height="25" src="{{ pool.logo }}" [alt]="pool.name + ' mining pool logo'" onError="this.src = './resources/mining-pools/default.svg'"> | ||||
|           </td> | ||||
|           <td class=""><a [routerLink]="[('/mining/pool/' + pool.slug) | relativeUrl]">{{ pool.name }}</a></td> | ||||
|           <td class="" *ngIf="this.miningWindowPreference === '24h' && !isLoading">{{ pool.lastEstimatedHashrate }} {{ | ||||
|             miningStats.miningUnits.hashrateUnit }}</td> | ||||
| @ -104,7 +104,7 @@ | ||||
|         <tr style="border-top: 1px solid #555"> | ||||
|           <td class="d-none d-md-block"></td> | ||||
|           <td class="text-right"></td> | ||||
|           <td class="" i18n="mining.all-miners"><b>All miners</b></td> | ||||
|           <td class=""><b i18n="mining.all-miners">All miners</b></td> | ||||
|           <td class="" *ngIf="this.miningWindowPreference === '24h'"><b>{{ miningStats.lastEstimatedHashrate}} {{ | ||||
|               miningStats.miningUnits.hashrateUnit }}</b></td> | ||||
|           <td class=""><b>{{ miningStats.blockCount }}</b></td> | ||||
| @ -121,7 +121,7 @@ | ||||
| <ng-template #loadingReward> | ||||
|   <div class="pool-distribution"> | ||||
|     <div class="item"> | ||||
|       <h5 class="card-title" i18n="mining.miners-luck">Pools luck (1w)</h5> | ||||
|       <h5 class="card-title" i18n="mining.miners-luck">Pools Luck (1w)</h5> | ||||
|       <p class="card-text"> | ||||
|         <span class="skeleton-loader skeleton-loader-big"></span> | ||||
|       </p> | ||||
| @ -133,7 +133,7 @@ | ||||
|       </p> | ||||
|     </div> | ||||
|     <div class="item"> | ||||
|       <h5 class="card-title" i18n="mining.miners-count">Pools count (1w)</h5> | ||||
|       <h5 class="card-title" i18n="mining.miners-count">Pools Count (1w)</h5> | ||||
|       <p class="card-text"> | ||||
|         <span class="skeleton-loader skeleton-loader-big"></span> | ||||
|       </p> | ||||
|  | ||||
| @ -27,7 +27,7 @@ | ||||
| .chart-widget { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   max-height: 270px; | ||||
|   height: 240px; | ||||
|   @media (max-width: 485px) { | ||||
|     max-height: 200px; | ||||
|   } | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|   <!-- Pool overview --> | ||||
|   <div *ngIf="poolStats$ | async as poolStats; else loadingMain"> | ||||
|     <div style="display:flex" class="mb-3"> | ||||
|       <img width="50" height="50" src="{{ poolStats['logo'] }}" | ||||
|       <img width="50" height="50" src="{{ poolStats['logo'] }}" [alt]="poolStats.pool.name + ' mining pool logo'" | ||||
|         onError="this.src = './resources/mining-pools/default.svg'" class="mr-3"> | ||||
|       <h1 class="m-0 pt-1 pt-md-0">{{ poolStats.pool.name }}</h1> | ||||
|     </div> | ||||
| @ -93,10 +93,8 @@ | ||||
|                   <table class="table table-xs table-data"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.estimated">Estimated | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.estimated">Estimated</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 26%" i18n="mining.luck">Luck</th> | ||||
|                       </tr> | ||||
|                     </thead> | ||||
| @ -117,10 +115,8 @@ | ||||
|                   <table class="table table-xs table-data"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
|                         <th scope="col" class="block-count-title" style="width: 33%" i18n="mining.estimated">Estimated | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 33%" i18n="mining.estimated">Estimated</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 30%" i18n="mining.luck">Luck</th> | ||||
|                       </tr> | ||||
|                     </thead> | ||||
| @ -142,7 +138,7 @@ | ||||
| 
 | ||||
|               <!-- Mined blocks desktop --> | ||||
|               <tr *ngIf="!isMobile()" class="taller-row"> | ||||
|                 <td class="label" i18n="mining.mined-blocks">Mined Blocks</td> | ||||
|                 <td class="label" i18n="mining.mined-blocks">Mined blocks</td> | ||||
|                 <td class="data"> | ||||
|                   <table class="table table-xs table-data"> | ||||
|                     <thead> | ||||
| @ -166,7 +162,7 @@ | ||||
|               <!-- Mined blocks mobile --> | ||||
|               <tr *ngIf="isMobile()"> | ||||
|                 <td colspan=2> | ||||
|                   <span class="label" i18n="mining.mined-blocks">Mined Blocks</span> | ||||
|                   <span class="label" i18n="mining.mined-blocks">Mined blocks</span> | ||||
|                   <table class="table table-xs table-data"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
| @ -216,12 +212,10 @@ | ||||
|         <th class="height" i18n="latest-blocks.height">Height</th> | ||||
|         <th class="timestamp" i18n="latest-blocks.timestamp">Timestamp</th> | ||||
|         <th class="mined" i18n="latest-blocks.mined">Mined</th> | ||||
|         <th class="coinbase text-left" i18n="latest-blocks.coinbasetag"> | ||||
|           Coinbase Tag</th> | ||||
|         <th class="reward text-right" i18n="latest-blocks.reward"> | ||||
|           Reward</th> | ||||
|         <th class="coinbase text-left" i18n="latest-blocks.coinbasetag">Coinbase tag</th> | ||||
|         <th class="reward text-right" i18n="latest-blocks.reward">Reward</th> | ||||
|         <th class="fees text-right" i18n="latest-blocks.fees">Fees</th> | ||||
|         <th class="txs text-right" i18n="latest-blocks.transactions">Txs</th> | ||||
|         <th class="txs text-right" i18n="dashboard.txs">TXs</th> | ||||
|         <th class="size" i18n="latest-blocks.size">Size</th> | ||||
|       </thead> | ||||
|       <tbody [style]="isLoading ? 'opacity: 0.75' : ''"> | ||||
| @ -266,12 +260,10 @@ | ||||
|         <th class="height" i18n="latest-blocks.height">Height</th> | ||||
|         <th class="timestamp" i18n="latest-blocks.timestamp">Timestamp</th> | ||||
|         <th class="mined" i18n="latest-blocks.mined">Mined</th> | ||||
|         <th class="coinbase text-left" i18n="latest-blocks.coinbasetag"> | ||||
|           Coinbase Tag</th> | ||||
|         <th class="reward text-right" i18n="latest-blocks.reward"> | ||||
|           Reward</th> | ||||
|         <th class="coinbase text-left" i18n="latest-blocks.coinbasetag">Coinbase tag</th> | ||||
|         <th class="reward text-right" i18n="latest-blocks.reward">Reward</th> | ||||
|         <th class="fees text-right" i18n="latest-blocks.fees">Fees</th> | ||||
|         <th class="txs text-right" i18n="latest-blocks.transactions">Txs</th> | ||||
|         <th class="txs text-right" i18n="dashboard.txs">TXs</th> | ||||
|         <th class="size" i18n="latest-blocks.size">Size</th> | ||||
|       </thead> | ||||
|       <tbody> | ||||
| @ -378,10 +370,8 @@ | ||||
|                   <table class="table table-xs table-data text-center"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.estimated">Estimated | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.estimated">Estimated</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 26%" i18n="mining.luck">Luck</th> | ||||
|                       </tr> | ||||
|                     </thead> | ||||
| @ -406,10 +396,8 @@ | ||||
|                   <table class="table table-xs table-data text-center"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
|                         <th scope="col" class="block-count-title" style="width: 33%" i18n="mining.estimated">Estimated | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported | ||||
|                         </th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 33%" i18n="mining.estimated">Estimated</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="mining.reported">Reported</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 30%" i18n="mining.luck">Luck</th> | ||||
|                       </tr> | ||||
|                     </thead> | ||||
| @ -430,13 +418,13 @@ | ||||
| 
 | ||||
|               <!-- Mined blocks desktop --> | ||||
|               <tr *ngIf="!isMobile()" class="taller-row"> | ||||
|                 <td class="label" i18n="mining.mined-blocks">Mined Blocks</td> | ||||
|                 <td class="label" i18n="mining.mined-blocks">Mined blocks</td> | ||||
|                 <td class="data"> | ||||
|                   <table class="table table-xs table-data text-center"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="24h">24h</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="1w">1w</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%">24h</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%">1w</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 26%" i18n="all">All</th> | ||||
|                       </tr> | ||||
|                     </thead> | ||||
| @ -457,12 +445,12 @@ | ||||
|               <!-- Mined blocks mobile --> | ||||
|               <tr *ngIf="isMobile()"> | ||||
|                 <td colspan=2> | ||||
|                   <span class="label" i18n="mining.mined-blocks">Mined Blocks</span> | ||||
|                   <span class="label" i18n="mining.mined-blocks">Mined blocks</span> | ||||
|                   <table class="table table-xs table-data text-center"> | ||||
|                     <thead> | ||||
|                       <tr> | ||||
|                         <th scope="col" class="block-count-title" style="width: 33%" i18n="24h">24h</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%" i18n="1w">1w</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 33%">24h</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 37%">1w</th> | ||||
|                         <th scope="col" class="block-count-title" style="width: 30%" i18n="all">All</th> | ||||
|                       </tr> | ||||
|                     </thead> | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| 
 | ||||
|   <form [formGroup]="pushTxForm" (submit)="pushTxForm.valid && postTx()" novalidate> | ||||
|     <div class="mb-3"> | ||||
|       <textarea formControlName="txHash" class="form-control" rows="5" i18n-placeholder="transaction.hex" placeholder="Transaction Hex"></textarea> | ||||
|       <textarea formControlName="txHash" class="form-control" rows="5" i18n-placeholder="transaction.hex" placeholder="Transaction hex"></textarea> | ||||
|     </div> | ||||
|     <button [disabled]="isLoading" type="submit" class="btn btn-primary mr-2" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</button> | ||||
|     <p class="red-color d-inline">{{ error }}</p> <a *ngIf="txId" [routerLink]="['/tx/' | relativeUrl, txId]">{{ txId }}</a> | ||||
|  | ||||
| @ -26,7 +26,7 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="item"> | ||||
|       <h5 class="card-title" i18n="mining.average-fee">Average Fee</h5> | ||||
|       <h5 class="card-title" i18n="mining.average-fee">Reward Per Tx</h5> | ||||
|       <div class="card-text" i18n-ngbTooltip="mining.average-fee" | ||||
|         ngbTooltip="Fee paid on average for each transaction in the past 144 blocks" placement="bottom"> | ||||
|         <div class="fee-text">{{ rewardStats.feePerTx | amountShortener: 2 }} | ||||
| @ -57,7 +57,7 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="item"> | ||||
|       <h5 class="card-title" i18n="mining.average-fee">Average Fee</h5> | ||||
|       <h5 class="card-title" i18n="mining.average-fee">Reward Per Tx</h5> | ||||
|       <div class="card-text"> | ||||
|         <div class="skeleton-loader"></div> | ||||
|         <div class="skeleton-loader"></div> | ||||
|  | ||||
| @ -46,7 +46,7 @@ | ||||
|             <table class="table table-borderless table-striped"> | ||||
|               <tbody> | ||||
|                 <tr> | ||||
|                   <td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td> | ||||
|                   <td i18n="block.timestamp">Timestamp</td> | ||||
|                   <td> | ||||
|                     ‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }} | ||||
|                     <div class="lg-inline"> | ||||
| @ -209,7 +209,7 @@ | ||||
|           <table class="table table-borderless table-striped"> | ||||
|             <tbody> | ||||
|               <tr> | ||||
|                 <td i18n="transaction.size|Transaction Size">Size</td> | ||||
|                 <td i18n="block.size">Size</td> | ||||
|                 <td [innerHTML]="'‎' + (tx.size | bytes: 2)"></td> | ||||
|               </tr> | ||||
|               <tr> | ||||
| @ -217,7 +217,7 @@ | ||||
|                 <td [innerHTML]="'‎' + (tx.weight / 4 | vbytes: 2)"></td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="transaction.weight|Transaction Weight">Weight</td> | ||||
|                 <td i18n="block.weight">Weight</td> | ||||
|                 <td [innerHTML]="'‎' + (tx.weight | wuBytes: 2)"></td> | ||||
|               </tr> | ||||
|             </tbody> | ||||
| @ -235,7 +235,7 @@ | ||||
|                 <td [innerHTML]="'‎' + (tx.locktime | number)"></td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="transaction.hex">Transaction Hex</td> | ||||
|                 <td i18n="transaction.hex">Transaction hex</td> | ||||
|                 <td><a target="_blank" href="{{ network === '' ? '' : '/' + network }}/api/tx/{{ txId }}/hex"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td> | ||||
|               </tr> | ||||
|             </tbody> | ||||
| @ -383,7 +383,7 @@ | ||||
|     <tbody> | ||||
|       <tr> | ||||
|         <td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td> | ||||
|         <td>{{ tx.fee | number }} <span class="symbol" i18n="transaction.fee.sat|Transaction Fee sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></td> | ||||
|         <td>{{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></td> | ||||
|       </tr> | ||||
|       <tr> | ||||
|         <td i18n="transaction.fee-rate|Transaction fee rate">Fee rate</td> | ||||
|  | ||||
| @ -141,7 +141,7 @@ | ||||
|             </ng-template> | ||||
|             <tr *ngIf="tx.vin.length > 12 && tx['@vinLimit']"> | ||||
|               <td colspan="3" class="text-center"> | ||||
|                 <button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;"><span i18n="transactions-list.load-all">Load all</span> ({{ tx.vin.length - 10 }})</button> | ||||
|                 <button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vin.length - 10 }})</button> | ||||
|               </td> | ||||
|             </tr> | ||||
|           </tbody> | ||||
| @ -238,7 +238,7 @@ | ||||
|                         <td style="text-align: left;">{{ vout.scriptpubkey }}</td> | ||||
|                       </tr> | ||||
|                       <tr *ngIf="vout.scriptpubkey_type == 'op_return'"> | ||||
|                         <td>OP_RETURN <span i18n="transactions-list.vout.scriptpubkey-type.data">data</span></td> | ||||
|                         <td>OP_RETURN <span>data</span></td> | ||||
|                         <td style="text-align: left;">{{ vout.scriptpubkey_asm | hex2ascii }}</td> | ||||
|                       </tr> | ||||
|                       <tr *ngIf="vout.scriptpubkey_type"> | ||||
| @ -252,7 +252,7 @@ | ||||
|             </ng-template> | ||||
|             <tr *ngIf="tx.vout.length > 12 && tx['@voutLimit'] && !outputIndex"> | ||||
|               <td colspan="3" class="text-center"> | ||||
|                 <button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="transactions-list.load-all">Load all</span> ({{ tx.vout.length - 10 }})</button> | ||||
|                 <button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vout.length - 10 }})</button> | ||||
|               </td> | ||||
|             </tr> | ||||
|           </tbody> | ||||
|  | ||||
| @ -121,7 +121,7 @@ | ||||
|                   <td *ngIf="!stateService.env.MINING_DASHBOARD" class="table-cell-mined" ><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td> | ||||
|                   <td *ngIf="stateService.env.MINING_DASHBOARD" class="table-cell-mined pl-lg-4"> | ||||
|                     <a class="clear-link" [routerLink]="[('/mining/pool/' + block.extras.pool.slug) | relativeUrl]"> | ||||
|                       <img width="20" height="20" src="{{ block.extras.pool['logo'] }}" | ||||
|                       <img width="22" height="22" src="{{ block.extras.pool['logo'] }}" | ||||
|                         onError="this.src = './resources/mining-pools/default.svg'"> | ||||
|                       <span class="pool-name">{{ block.extras.pool.name }}</span> | ||||
|                     </a> | ||||
| @ -136,7 +136,7 @@ | ||||
|                 </tr> | ||||
|               </tbody> | ||||
|             </table> | ||||
|             <div class=""><a href="" [routerLink]="[(stateService.env.MINING_DASHBOARD ? '/mining/blocks' : '/blocks') | relativeUrl]" i18n="dashboard.view-all">View all »</a></div> | ||||
|             <div class=""><a href="" [routerLink]="[(stateService.env.MINING_DASHBOARD ? '/mining/blocks' : '/blocks') | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
| @ -134,6 +134,8 @@ | ||||
|   table-layout:fixed; | ||||
|   tr, td, th { | ||||
|     border: 0px; | ||||
|     padding-top: 0.71rem !important; | ||||
|     padding-bottom: 0.75rem !important; | ||||
|   } | ||||
|   td { | ||||
|     overflow:hidden; | ||||
| @ -182,16 +184,15 @@ | ||||
|   text-align: left; | ||||
|   tr, td, th { | ||||
|     border: 0px; | ||||
|     padding-top: 0.65rem !important; | ||||
|     padding-bottom: 0.7rem !important; | ||||
|   } | ||||
|   .table-cell-height { | ||||
|     width: 15%; | ||||
|   } | ||||
|   .table-cell-mined { | ||||
|     width: 35%; | ||||
|     text-align: right; | ||||
|     @media (min-width: 376px) { | ||||
|       text-align: left; | ||||
|     } | ||||
|     text-align: left; | ||||
|   } | ||||
|   .table-cell-transaction-count { | ||||
|     display: none; | ||||
|  | ||||
| @ -4467,6 +4467,14 @@ export const faqData = [ | ||||
|     title: "What are mining pools?", | ||||
|     answer: "Mining pools are groups of miners that combine their computational power in order to increase the probability of finding new blocks." | ||||
|   }, | ||||
|   { | ||||
|     type: "endpoint", | ||||
|     category: "basics", | ||||
|     showConditions: bitcoinNetworks, | ||||
|     fragment: "what-is-full-mempool", | ||||
|     title: "What does it mean for the mempool to be \"full\"?", | ||||
|     answer: "<p>When a Bitcoin transaction is made, it is stored in a Bitcoin node's mempool before it is confirmed into a block. When the rate of incoming transactions exceeds the rate transactions are confirmed, the mempool grows in size.</p><p>The default maximum size of a Bitcoin node's mempool is 300MB, so when there are 300MB of transactions in the mempool, we say it's \"full\".</p>" | ||||
|   }, | ||||
|   { | ||||
|     type: "category", | ||||
|     category: "help", | ||||
|  | ||||
| @ -154,6 +154,10 @@ export class ApiService { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   getBlock$(hash: string): Observable<BlockExtended> { | ||||
|     return this.httpClient.get<BlockExtended>(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash); | ||||
|   } | ||||
| 
 | ||||
|   getHistoricalHashrate$(interval: string | undefined): Observable<any> { | ||||
|     return this.httpClient.get<any[]>( | ||||
|         this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/hashrate` + | ||||
|  | ||||
| @ -14,7 +14,6 @@ export class AssetsService { | ||||
| 
 | ||||
|   getAssetsJson$: Observable<{ array: AssetExtended[]; objects: any}>; | ||||
|   getAssetsMinimalJson$: Observable<any>; | ||||
|   getMiningPools$: Observable<any>; | ||||
| 
 | ||||
|   constructor( | ||||
|     private httpClient: HttpClient, | ||||
| @ -66,6 +65,5 @@ export class AssetsService { | ||||
|       }), | ||||
|       shareReplay(1), | ||||
|     ); | ||||
|     this.getMiningPools$ = this.httpClient.get(apiBaseUrl + '/resources/pools.json').pipe(shareReplay(1)); | ||||
|   } | ||||
| } | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -82,11 +82,11 @@ pkg install -y zsh sudo git screen curl wget neovim rsync nginx openssl openssh- | ||||
| 
 | ||||
| ### Node.js + npm | ||||
| 
 | ||||
| Build Node.js v16.10 and npm v7 from source using `nvm`: | ||||
| Build Node.js v16.15 and npm v8 from source using `nvm`: | ||||
| ``` | ||||
| curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh | ||||
| source $HOME/.zshrc | ||||
| nvm install v16.10.0 | ||||
| nvm install v16.15.0 | ||||
| nvm alias default node | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| @ -854,7 +854,7 @@ echo "[*] Installing nvm.sh from GitHub" | ||||
| osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh' | ||||
| 
 | ||||
| echo "[*] Building NodeJS via nvm.sh" | ||||
| osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm install v16.10.0' | ||||
| osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm install v16.15.0' | ||||
| 
 | ||||
| #################### | ||||
| # Tor installation # | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user