mirror of
https://github.com/mempool/mempool.git
synced 2024-11-19 01:41:01 +01:00
add stratum job to pool page
This commit is contained in:
parent
bb3f70ab1c
commit
f5ff97650a
@ -10,7 +10,7 @@
|
||||
<h1 class="m-0 pt-1 pt-md-0">{{ poolStats.pool.name }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="box pool-details">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-6">
|
||||
@ -173,7 +173,119 @@
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
<!-- Stratum Job -->
|
||||
<ng-container *ngIf="(job$ | async) as job;">
|
||||
<h2 i18n="pool.next_block">Next block</h2>
|
||||
<div class="box mb-3">
|
||||
<div class="row" >
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="job-table table table-xs table-borderless table-fixed table-data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="data-title clip text-center height" i18n="latest-blocks.height">Height</th>
|
||||
<th class="data-title clip text-center expected" i18n="next-block.expected-time">Expected</th>
|
||||
<th class="data-title clip text-center reward" i18n="latest-blocks.reward">Reward</th>
|
||||
<th class="data-title clip text-center timestamp" i18n="next-block.timestamp">Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center height">
|
||||
{{ job.height }}
|
||||
</td>
|
||||
<td class="text-center expected">
|
||||
<ng-container *ngIf="(expectedBlockTime$ | async) as expectedBlockTime; else expectedPlaceholder">
|
||||
<app-time kind="until" [time]="expectedBlockTime" [fastRender]="false" [fixedRender]="true" [precision]="1" minUnit="minute"></app-time>
|
||||
</ng-container>
|
||||
<ng-template #expectedPlaceholder>~</ng-template>
|
||||
</td>
|
||||
<td class="text-center reward">
|
||||
<app-amount [satoshis]="job.reward"></app-amount>
|
||||
</td>
|
||||
<td class="text-center timestamp">
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="job.timestamp" [precision]="1" minUnit="minute" [hideTimeSince]="true"></app-timestamp>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="job-table table table-xs table-borderless table-fixed table-data">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="data-title clip text-center coinbase" i18n="latest-blocks.coinbasetag">Coinbase tag</th>
|
||||
<th class="data-title clip text-center clean" i18n="next-block.clean">Clean</th>
|
||||
<th class="data-title clip text-center prevhash" i18n="next-block.prevhash">Prevhash</th>
|
||||
<th class="data-title clip text-center job-received" i18n="next-block.job-received">Job Received</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center coinbase">
|
||||
{{ job.scriptsig | hex2ascii }}
|
||||
</td>
|
||||
<td class="text-center clean">
|
||||
@if (job.cleanJobs) {
|
||||
<fa-icon [icon]="['fas', 'check-circle']" [fixedWidth]="true"></fa-icon>
|
||||
} @else {
|
||||
<fa-icon [icon]="['fas', 'times-circle']" [fixedWidth]="true"></fa-icon>
|
||||
}
|
||||
</td>
|
||||
<td class="text-center prevhash">
|
||||
<a [routerLink]="['/block' | relativeUrl, job.prevHash]">
|
||||
<app-truncate [text]="job.prevHash" [lastChars]="8"></app-truncate>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center job-received">
|
||||
<app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="job.received / 1000" [precision]="1" minUnit="minute" [hideTimeSince]="true"></app-timestamp>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="stratum-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="data-title clip text-center" [attr.colspan]="Math.max(job.merkleBranches.length, 12)">
|
||||
<a class="title-link" href="" [routerLink]="['/stratum' | relativeUrl]">
|
||||
Merkle Branches
|
||||
<span> </span>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
@for (branch of job.merkleBranches; track $index) {
|
||||
<td class="merkle" [style.background-color]="branch ? '#' + branch.slice(0, 6) : ''"></td>
|
||||
}
|
||||
@for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
|
||||
<td class="merkle empty-branch"></td>
|
||||
}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Blocks list -->
|
||||
<h2 i18n="master-page.blocks">Blocks</h2>
|
||||
<table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5"
|
||||
[infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
|
||||
<ng-container *ngIf="blocks$ | async as blocks; else skeleton">
|
||||
|
@ -49,111 +49,110 @@ div.scrollable {
|
||||
max-height: 75px;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding-bottom: 5px;
|
||||
.pool-details {
|
||||
@media (min-width: 767.98px) {
|
||||
min-height: 187px;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 25%;
|
||||
@media (min-width: 767.98px) {
|
||||
vertical-align: middle;
|
||||
.label {
|
||||
width: 25%;
|
||||
@media (min-width: 767.98px) {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
font-weight: bold;
|
||||
.label.addresses {
|
||||
vertical-align: top;
|
||||
padding-top: 25px;
|
||||
}
|
||||
.addresses-data {
|
||||
vertical-align: top;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.label.addresses {
|
||||
vertical-align: top;
|
||||
padding-top: 25px;
|
||||
}
|
||||
.addresses-data {
|
||||
vertical-align: top;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.data {
|
||||
text-align: right;
|
||||
padding-left: 5%;
|
||||
@media (max-width: 992px) {
|
||||
text-align: left;
|
||||
padding-left: 12px;
|
||||
}
|
||||
@media (max-width: 450px) {
|
||||
.data {
|
||||
text-align: right;
|
||||
padding-left: 5%;
|
||||
@media (max-width: 992px) {
|
||||
text-align: left;
|
||||
padding-left: 12px;
|
||||
}
|
||||
@media (max-width: 450px) {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
.progress {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.coinbase {
|
||||
width: 20%;
|
||||
@media (max-width: 875px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.height {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
@media (max-width: 875px) {
|
||||
padding-left: 50px;
|
||||
}
|
||||
@media (max-width: 685px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mined {
|
||||
width: 13%;
|
||||
@media (max-width: 1100px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.txs {
|
||||
padding-right: 40px;
|
||||
@media (max-width: 1100px) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
@media (max-width: 875px) {
|
||||
padding-right: 20px;
|
||||
}
|
||||
@media (max-width: 567px) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.size {
|
||||
width: 12%;
|
||||
@media (max-width: 1000px) {
|
||||
width: 15%;
|
||||
}
|
||||
@media (max-width: 875px) {
|
||||
.coinbase {
|
||||
width: 20%;
|
||||
@media (max-width: 875px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 650px) {
|
||||
width: 20%;
|
||||
}
|
||||
@media (max-width: 450px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.scriptmessage {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
.height {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
@media (max-width: 875px) {
|
||||
padding-left: 50px;
|
||||
}
|
||||
@media (max-width: 685px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mined {
|
||||
width: 13%;
|
||||
@media (max-width: 1100px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.txs {
|
||||
padding-right: 40px;
|
||||
@media (max-width: 1100px) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
@media (max-width: 875px) {
|
||||
padding-right: 20px;
|
||||
}
|
||||
@media (max-width: 567px) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.size {
|
||||
width: 12%;
|
||||
@media (max-width: 1000px) {
|
||||
width: 15%;
|
||||
}
|
||||
@media (max-width: 875px) {
|
||||
width: 20%;
|
||||
}
|
||||
@media (max-width: 650px) {
|
||||
width: 20%;
|
||||
}
|
||||
@media (max-width: 450px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.scriptmessage {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-loader {
|
||||
@ -214,4 +213,55 @@ div.scrollable {
|
||||
|
||||
.taller-row {
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
.stratum-table {
|
||||
width: 100%;
|
||||
|
||||
.merkle {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.empty-branch {
|
||||
outline: solid 1px white;
|
||||
outline-offset: -1px;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: linear-gradient(to top left, transparent, transparent 48%, white 49%, white 51%, transparent 52%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
height: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.job-table {
|
||||
td, th {
|
||||
width: 25%;
|
||||
max-width: 25%;
|
||||
min-width: 25%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0.1rem 0.2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.expected, .timestamp, .clean, .job-received {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-link, .title-link:hover, .title-link:focus, .title-link:active {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
@ -10,6 +10,9 @@ import { selectPowerOfTen } from '@app/bitcoin.utils';
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { SeoService } from '@app/services/seo.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { StratumJob } from '../../interfaces/websocket.interface';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { MiningService } from '../../services/mining.service';
|
||||
|
||||
interface AccelerationTotal {
|
||||
cost: number,
|
||||
@ -27,12 +30,16 @@ export class PoolComponent implements OnInit {
|
||||
@Input() left: number | string = 75;
|
||||
|
||||
gfg = true;
|
||||
stratumEnabled = this.stateService.env.STRATUM_ENABLED;
|
||||
|
||||
formatNumber = formatNumber;
|
||||
Math = Math;
|
||||
slugSubscription: Subscription;
|
||||
poolStats$: Observable<PoolStat>;
|
||||
blocks$: Observable<BlockExtended[]>;
|
||||
oobFees$: Observable<AccelerationTotal[]>;
|
||||
job$: Observable<StratumJob | null>;
|
||||
expectedBlockTime$: Observable<number>;
|
||||
isLoading = true;
|
||||
error: HttpErrorResponse | null = null;
|
||||
|
||||
@ -53,6 +60,8 @@ export class PoolComponent implements OnInit {
|
||||
private apiService: ApiService,
|
||||
private route: ActivatedRoute,
|
||||
public stateService: StateService,
|
||||
private websocketService: WebsocketService,
|
||||
private miningService: MiningService,
|
||||
private seoService: SeoService,
|
||||
) {
|
||||
this.auditAvailable = this.stateService.env.AUDIT;
|
||||
@ -62,7 +71,7 @@ export class PoolComponent implements OnInit {
|
||||
this.slugSubscription = this.route.params.pipe(map((params) => params.slug)).subscribe((slug) => {
|
||||
this.isLoading = true;
|
||||
this.blocks = [];
|
||||
this.chartOptions = {};
|
||||
this.chartOptions = {};
|
||||
this.slug = slug;
|
||||
this.initializeObservables();
|
||||
});
|
||||
@ -129,6 +138,31 @@ export class PoolComponent implements OnInit {
|
||||
}),
|
||||
filter(oob => oob.length === 3 && oob[2].count > 0)
|
||||
);
|
||||
|
||||
if (this.stratumEnabled) {
|
||||
this.job$ = combineLatest([
|
||||
this.poolStats$.pipe(
|
||||
tap((poolStats) => {
|
||||
this.websocketService.startTrackStratum(poolStats.pool.unique_id);
|
||||
})
|
||||
),
|
||||
this.stateService.stratumJobs$
|
||||
]).pipe(
|
||||
map(([poolStats, jobs]) => {
|
||||
return jobs[poolStats.pool.unique_id];
|
||||
})
|
||||
);
|
||||
|
||||
this.expectedBlockTime$ = combineLatest([
|
||||
this.miningService.getMiningStats('1w'),
|
||||
this.poolStats$,
|
||||
this.stateService.difficultyAdjustment$
|
||||
]).pipe(
|
||||
map(([miningStats, poolStat, da]) => {
|
||||
return (da.timeAvg / ((poolStat.estimatedHashrate || 0) / (miningStats.lastEstimatedHashrate * 1_000_000_000_000_000_000))) + Date.now() + da.timeOffset;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
prepareChartOptions(hashrate, share) {
|
||||
|
Loading…
Reference in New Issue
Block a user