diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html
index b3c6430a8..faa0003c4 100644
--- a/frontend/src/app/components/pool/pool.component.html
+++ b/frontend/src/app/components/pool/pool.component.html
@@ -10,7 +10,7 @@
{{ poolStats.pool.name }}
-
+
+
+
+ Next block
+
+
+
+
+
+
+
+
+
+
+ Height |
+ Expected |
+ Reward |
+ Timestamp |
+
+
+
+
+
+ {{ job.height }}
+ |
+
+
+
+
+ ~
+ |
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+ Coinbase tag |
+ Clean |
+ Prevhash |
+ Job Received |
+
+
+
+
+
+ {{ job.scriptsig | hex2ascii }}
+ |
+
+ @if (job.cleanJobs) {
+
+ } @else {
+
+ }
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Merkle Branches
+
+
+
+ |
+
+
+
+
+ @for (branch of job.merkleBranches; track $index) {
+ |
+ }
+ @for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
+ |
+ }
+
+
+
+ |
+
+
+
+
+
+
+
+
+
Blocks
diff --git a/frontend/src/app/components/pool/pool.component.scss b/frontend/src/app/components/pool/pool.component.scss
index 5c2fedd26..31d12474f 100644
--- a/frontend/src/app/components/pool/pool.component.scss
+++ b/frontend/src/app/components/pool/pool.component.scss
@@ -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;
}
\ No newline at end of file
diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts
index 1893f0a48..23b795613 100644
--- a/frontend/src/app/components/pool/pool.component.ts
+++ b/frontend/src/app/components/pool/pool.component.ts
@@ -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;
blocks$: Observable;
oobFees$: Observable;
+ job$: Observable;
+ expectedBlockTime$: Observable;
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) {
diff --git a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
index 1ab1a1c94..0af9f0976 100644
--- a/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
+++ b/frontend/src/app/components/stratum/stratum-list/stratum-list.component.ts
@@ -36,13 +36,14 @@ function parseTag(scriptSig: string): string {
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
- const ascii = new TextDecoder('utf8').decode(Uint8Array.from(bytes)).replace(/\uFFFD/g, '').replace(/\\0/g, '');
+ // eslint-disable-next-line no-control-regex
+ const ascii = new TextDecoder('utf8').decode(Uint8Array.from(bytes)).replace(/\uFFFD/g, '').replace(/\\0/g, '').replace(/[\x00-\x1F\x7F-\x9F]/g, '');
if (ascii.includes('/ViaBTC/')) {
return '/ViaBTC/';
} else if (ascii.includes('SpiderPool/')) {
return 'SpiderPool/';
}
- return ascii.match(/\/.*\//)?.[0] || ascii;
+ return (ascii.match(/\/.*\//)?.[0] || ascii).trim();
}
@Component({