diff --git a/backend/src/interfaces.ts b/backend/src/interfaces.ts index 0360f727b..c8b7cdd0c 100644 --- a/backend/src/interfaces.ts +++ b/backend/src/interfaces.ts @@ -76,7 +76,7 @@ export interface Vout { scriptpubkey: string; scriptpubkey_asm: string; scriptpubkey_type: string; - scriptpubkey_address: string; + scriptpubkey_address?: string; value: number; asset?: string; diff --git a/frontend/.gitignore b/frontend/.gitignore index 7063e22de..1b086fca2 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -47,3 +47,4 @@ Thumbs.db src/resources/assets.json src/resources/assets.minimal.json +src/resources/pools.json diff --git a/frontend/package.json b/frontend/package.json index 71591436f..473966eb8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,8 +23,8 @@ "ng": "ng", "start": "npm run sync-assets-dev && ng serve --proxy-config proxy.conf.json", "build": "ng build --prod && npm run sync-assets", - "sync-assets": "node sync-asset-registry.js", - "sync-assets-dev": "node sync-asset-registry.js dev", + "sync-assets": "node sync-assets.js", + "sync-assets-dev": "node sync-assets.js dev", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index bfbac5ec0..6052b4c68 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -49,6 +49,7 @@ import { AssetComponent } from './components/asset/asset.component'; import { ScriptpubkeyTypePipe } from './pipes/scriptpubkey-type-pipe/scriptpubkey-type.pipe'; import { AssetsComponent } from './assets/assets.component'; import { RelativeUrlPipe } from './pipes/relative-url/relative-url.pipe'; +import { MinerComponent } from './pipes/miner/miner.component'; @NgModule({ declarations: [ @@ -88,6 +89,7 @@ import { RelativeUrlPipe } from './pipes/relative-url/relative-url.pipe'; ScriptpubkeyTypePipe, AssetsComponent, RelativeUrlPipe, + MinerComponent, ], imports: [ BrowserModule, diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 50600233b..41c8cfd74 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -49,12 +49,12 @@ Total fees - () + () Reward + fees: - () + () @@ -68,6 +68,10 @@ + + Miner + + diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 3f60e6b95..e815fceed 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -37,6 +37,7 @@ export class BlockComponent implements OnInit, OnDestroy { .pipe( switchMap((params: ParamMap) => { const blockHash: string = params.get('id') || ''; + this.block = undefined; this.error = undefined; this.fees = undefined; diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss index 819b6eb77..c78f6ec37 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss @@ -57,7 +57,7 @@ width: 100%; left: -12px; text-shadow: 0px 32px 3px #111; - z-index: 100; + z-index: 1; } .bitcoin-block::after { diff --git a/frontend/src/app/pipes/miner/miner.component.html b/frontend/src/app/pipes/miner/miner.component.html new file mode 100644 index 000000000..895e36622 --- /dev/null +++ b/frontend/src/app/pipes/miner/miner.component.html @@ -0,0 +1,12 @@ + + + + + + + {{ miner }} + + + Unknown + + diff --git a/frontend/src/app/pipes/miner/miner.component.scss b/frontend/src/app/pipes/miner/miner.component.scss new file mode 100644 index 000000000..b6e8c8ca1 --- /dev/null +++ b/frontend/src/app/pipes/miner/miner.component.scss @@ -0,0 +1,3 @@ +.badge { + font-size: 14px; +} diff --git a/frontend/src/app/pipes/miner/miner.component.ts b/frontend/src/app/pipes/miner/miner.component.ts new file mode 100644 index 000000000..6e33c5f9c --- /dev/null +++ b/frontend/src/app/pipes/miner/miner.component.ts @@ -0,0 +1,68 @@ +import { Component, Input, OnChanges } from '@angular/core'; +import { AssetsService } from 'src/app/services/assets.service'; +import { Transaction } from 'src/app/interfaces/electrs.interface'; + +@Component({ + selector: 'app-miner', + templateUrl: './miner.component.html', + styleUrls: ['./miner.component.scss'], +}) +export class MinerComponent implements OnChanges { + @Input() coinbaseTransaction: Transaction; + miner = ''; + title = ''; + url = ''; + loading = true; + + constructor( + private assetsService: AssetsService, + ) { } + + ngOnChanges() { + 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 = 'Identified by payout address: ' + vout.scriptpubkey_address; + this.url = pools.payout_addresses[vout.scriptpubkey_address].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) { + this.miner = pools.coinbase_tags[tag].name; + this.title = 'Identified by coinbase tag: ' + tag; + this.url = pools.coinbase_tags[tag].link; + break; + } + } + } + } + + this.loading = false; + }); + } + + 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; + } +} diff --git a/frontend/src/app/services/assets.service.ts b/frontend/src/app/services/assets.service.ts index 4023f048d..2adf57211 100644 --- a/frontend/src/app/services/assets.service.ts +++ b/frontend/src/app/services/assets.service.ts @@ -9,11 +9,13 @@ import { shareReplay } from 'rxjs/operators'; export class AssetsService { getAssetsJson$: Observable; getAssetsMinimalJson$: Observable; + getMiningPools$: Observable; constructor( private httpClient: HttpClient, ) { this.getAssetsJson$ = this.httpClient.get('/resources/assets.json').pipe(shareReplay()); this.getAssetsMinimalJson$ = this.httpClient.get('/resources/assets.minimal.json').pipe(shareReplay()); + this.getMiningPools$ = this.httpClient.get('/resources/pools.json').pipe(shareReplay()); } } diff --git a/frontend/sync-asset-registry.js b/frontend/sync-assets.js similarity index 80% rename from frontend/sync-asset-registry.js rename to frontend/sync-assets.js index 0b56cfcbc..304eae57a 100644 --- a/frontend/sync-asset-registry.js +++ b/frontend/sync-assets.js @@ -17,3 +17,5 @@ console.log('Downloading assets'); download(PATH + 'assets.json', 'https://raw.githubusercontent.com/Blockstream/asset_registry_db/master/index.json'); console.log('Downloading assets minimal'); download(PATH + 'assets.minimal.json', 'https://raw.githubusercontent.com/Blockstream/asset_registry_db/master/index.minimal.json'); +console.log('Downloading mining pools info'); +download(PATH + 'pools.json', 'https://raw.githubusercontent.com/btccom/Blockchain-Known-Pools/master/pools.json'); \ No newline at end of file