Generate mining basic pool ranking (sorted by block found) for a specified timeframe

This commit is contained in:
nymkappa
2022-01-06 19:59:33 +09:00
parent 4646cc6df3
commit 5ca98af7d1
16 changed files with 366 additions and 48 deletions

View File

@@ -0,0 +1,68 @@
<div class="container-xl">
<div class="card-header">
<i class="fa fa-area-chart"></i> <span i18n="mining.pools-by-vBytes">Pools</span>
<form [formGroup]="radioGroupForm" class="formRadioGroup" (click)="savePoolsPreference()">
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'1d'" [routerLink]="['/pools' | relativeUrl]" fragment="1d"> 1D
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'3d'" [routerLink]="['/pools' | relativeUrl]" fragment="3d"> 3D
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'1w'" [routerLink]="['/pools' | relativeUrl]" fragment="1w"> 1W
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'1m'" [routerLink]="['/pools' | relativeUrl]" fragment="1m"> 1M
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'3m'" [routerLink]="['/pools' | relativeUrl]" fragment="3m"> 3M
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'6m'" [routerLink]="['/pools' | relativeUrl]" fragment="6m"> 6M
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'1y'" [routerLink]="['/pools' | relativeUrl]" fragment="1y"> 1Y
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'2y'" [routerLink]="['/pools' | relativeUrl]" fragment="2y"> 2Y
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'3y'" [routerLink]="['/pools' | relativeUrl]" fragment="3y"> 3Y
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'all'" [routerLink]="['/pools' | relativeUrl]" fragment="all"> ALL
</label>
</div>
</form>
</div>
<table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50">
<thead>
<th i18n="latest-blocks.height">Rank</th>
<th class="d-none d-md-block"i18n="latest-blocks.timestamp">Name</th>
<th i18n="latest-blocks.timestamp">Hashrate</th>
<th class="d-none d-md-block" i18n="latest-blocks.mined">Block Count (share)</th>
<th i18n="latest-blocks.transactions">Empty Blocks (ratio)</th>
</thead>
<tbody *ngIf="pools$ | async as pools">
<tr>
<td>-</td>
<td>All miners</td>
<td>{{ pools["lastEstimatedHashrate"]}} PH/s</td>
<td>{{ pools["blockCount"] }}</td>
<td>{{ pools["totalEmptyBlock"] }} ({{ pools["totalEmptyBlockRatio"] }}%)</td>
</tr>
<ng-template ngFor let-pool [ngForOf]="pools['poolsStats']">
<tr>
<td>{{ pool.rank }}</td>
<td><a href="{{ pool.link }}">{{ pool.name }}</a></td>
<td>{{ pool.lastEstimatedHashrate }} PH/s</td>
<td>{{ pool.blockCount }} ({{ pool.share }}%)</td>
<td>{{ pool.emptyBlocks }} ({{ pool.emptyBlockRatio }}%)</td>
</tr>
</ng-template>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,103 @@
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { merge, Observable, ObservableInput, of } from 'rxjs';
import { map, share, switchMap, tap } from 'rxjs/operators';
import { PoolsStats } from 'src/app/interfaces/node-api.interface';
import { StorageService } from 'src/app/services/storage.service';
import { ApiService } from '../../services/api.service';
@Component({
selector: 'app-pool-ranking',
templateUrl: './pool-ranking.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PoolRankingComponent implements OnInit {
pools$: Observable<object>
radioGroupForm: FormGroup;
poolsWindowPreference: string;
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private apiService: ApiService,
private storageService: StorageService,
) { }
ngOnInit(): void {
this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference').trim() : '2h';
this.radioGroupForm = this.formBuilder.group({
dateSpan: this.poolsWindowPreference
});
// Setup datespan triggers
this.route.fragment.subscribe((fragment) => {
if (['1d', '3d', '1w', '1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) {
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
}
});
merge(of(''), this.radioGroupForm.controls.dateSpan.valueChanges)
.pipe(switchMap(() => this.onDateSpanChanged()))
.subscribe((pools: any) => {
console.log(pools);
});
// Fetch initial mining pool data
this.onDateSpanChanged();
}
ngOnChanges() {
}
rendered() {
}
savePoolsPreference() {
this.storageService.setValue('poolsWindowPreference', this.radioGroupForm.controls.dateSpan.value);
this.poolsWindowPreference = this.radioGroupForm.controls.dateSpan.value;
}
onDateSpanChanged(): ObservableInput<any> {
let interval: string;
console.log(this.poolsWindowPreference);
switch (this.poolsWindowPreference) {
case '1d': interval = '1 DAY'; break;
case '3d': interval = '3 DAY'; break;
case '1w': interval = '1 WEEK'; break;
case '1m': interval = '1 MONTH'; break;
case '3m': interval = '3 MONTH'; break;
case '6m': interval = '6 MONTH'; break;
case '1y': interval = '1 YEAR'; break;
case '2y': interval = '2 YEAR'; break;
case '3y': interval = '3 YEAR'; break;
case 'all': interval = '1000 YEAR'; break;
}
this.pools$ = this.apiService.listPools$(interval).pipe(map(res => this.computeMiningStats(res)));
return this.pools$;
}
computeMiningStats(stats: PoolsStats) {
const totalEmptyBlock = Object.values(stats.poolsStats).reduce((prev, cur) => {
return prev + cur.emptyBlocks;
}, 0);
const totalEmptyBlockRatio = (totalEmptyBlock / stats.blockCount * 100).toFixed(2);
const poolsStats = stats.poolsStats.map((poolStat) => {
return {
share: (poolStat.blockCount / stats.blockCount * 100).toFixed(2),
lastEstimatedHashrate: (poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / Math.pow(10, 15)).toFixed(2),
emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
...poolStat
}
});
return {
lastEstimatedHashrate: (stats.lastEstimatedHashrate / Math.pow(10, 15)).toFixed(2),
blockCount: stats.blockCount,
totalEmptyBlock: totalEmptyBlock,
totalEmptyBlockRatio: totalEmptyBlockRatio,
poolsStats: poolsStats,
}
}
}