Skip to content
This repository was archived by the owner on May 25, 2026. It is now read-only.

Commit 2a3a75f

Browse files
Fix Xmrig/P2Pool mining
1 parent 08cb853 commit 2a3a75f

8 files changed

Lines changed: 86 additions & 35 deletions

File tree

app/process/XmrigProcess.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AppChildProcess, sudo } from "./AppChildProcess";
22

33
export class XmrigProcess extends AppChildProcess {
44

5-
protected static readonly stdoutPattern: string = `READY`;
5+
protected static readonly stdoutPattern: string = `* COMMANDS`;
66

77
constructor({ cmd, flags, isExe }: { cmd: string, flags?: string[], isExe?: boolean }) {
88
super({
@@ -108,12 +108,16 @@ export class XmrigProcess extends AppChildProcess {
108108
return;
109109
}
110110

111-
timeout = setTimeout(() => {
111+
timeout = setTimeout(async () => {
112112
if (this._process && this._process.exitCode == null) {
113113
this._process.kill();
114114
}
115115
timeout = undefined;
116116

117+
if (this._process) {
118+
await this.kill();
119+
}
120+
117121
reject(new Error("XmrigProcess.start(): Timed out"));
118122
}, 60*1000);
119123
}
@@ -192,24 +196,14 @@ export class XmrigProcess extends AppChildProcess {
192196
}
193197
}
194198

195-
public async stop(): Promise<number | null> {
196-
if (this._starting) {
197-
throw new Error("Process is starting");
198-
}
199-
200-
if (this._stopping) {
201-
throw new Error("Process is already stopping");
202-
}
203-
204-
const proc = this._process;
205-
206-
if (!this._running || !proc) {
207-
throw new Error("Process is not running");
208-
}
209-
210-
this._stopping = true;
211-
199+
private async kill(): Promise<number | null> {
212200
const promise = new Promise<number | null>((resolve) => {
201+
const proc = this._process;
202+
if (!proc) {
203+
console.error("Could not kill xmrig process, not found");
204+
return;
205+
}
206+
213207
proc.on('close', (code: number | null) => {
214208
this._process = undefined;
215209
this._running = false;
@@ -232,6 +226,26 @@ export class XmrigProcess extends AppChildProcess {
232226
return await promise;
233227
}
234228

229+
public async stop(): Promise<number | null> {
230+
if (this._starting) {
231+
throw new Error("Process is starting");
232+
}
233+
234+
if (this._stopping) {
235+
throw new Error("Process is already stopping");
236+
}
237+
238+
const proc = this._process;
239+
240+
if (!this._running || !proc) {
241+
throw new Error("Process is not running");
242+
}
243+
244+
this._stopping = true;
245+
246+
return await this.kill();
247+
}
248+
235249
public async getVersion(): Promise<string> {
236250
const proc = new XmrigProcess({
237251
cmd: this._command,

src/app/core/services/daemon/daemon-data.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ export class DaemonDataService {
339339
const i = this.info;
340340
await this.xmrigService.refreshMiningStatus(i ? i.difficulty : 0);
341341
this._miningStatus = this.xmrigService.miningStatus;
342-
342+
343+
if (this.p2poolService.running) await this.p2poolService.updateData();
343344
}
344345
else if (this.p2poolService.running) {
345346
await this.p2poolService.updateData();

src/app/core/services/daemon/daemon.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,7 @@ export class DaemonService {
14361436
}
14371437

14381438
public getGuiVersion(): string {
1439-
return "1.3.0 Fortitude";
1439+
return "1.3.1 Fortitude";
14401440
}
14411441

14421442
public async getProcessStats(): Promise<ProcessStats> {

src/app/core/services/p2pool/p2pool.service.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { EventEmitter, inject, Injectable } from '@angular/core';
22
import { MiningStatus, P2PoolSettings } from '../../../../common';
33
import { IDBPDatabase, openDB } from 'idb';
44
import { ElectronService } from '../electron/electron.service';
5+
import { Subscription } from 'rxjs';
56

67
type Status = 'running' | 'stopped' | 'starting' | 'restarting' | 'stopping';
78
type Pool = 'main' | 'mini';
@@ -170,8 +171,10 @@ export class P2poolService {
170171
private _loaded: boolean = false;
171172
private _logs: string[] = [];
172173
private _startedAt = 0;
174+
private _synchronized: boolean = false;
173175
public readonly onStart: EventEmitter<void> = new EventEmitter<void>();
174176
public readonly onStop: EventEmitter<void> = new EventEmitter<void>();
177+
public readonly onSynced: EventEmitter<void> = new EventEmitter<void>();
175178

176179
// Cached data
177180

@@ -354,6 +357,7 @@ export class P2poolService {
354357
window.electronAPI.onP2PoolOutput(({stdout, stderr} : { stdout?: string, stderr?: string }) => {
355358
if (stdout) {
356359
this._logs.push(stdout);
360+
if (stdout.includes('SideChain SYNCHRONIZED')) this.onSynced.emit();
357361
this.std.out.emit(stdout);
358362
} else if (stderr) {
359363
this._logs.push(stderr);
@@ -392,6 +396,20 @@ export class P2poolService {
392396
if (err) throw err;
393397
}
394398

399+
public async waitForSidechainSync(): Promise<void> {
400+
if (this.status === 'stopped' || this.status === 'stopping') throw new Error("P2Pool service not running")
401+
if (this.synchronized) return;
402+
403+
await new Promise<void>((resolve) => {
404+
let sub: Subscription;
405+
406+
sub = this.onSynced.subscribe(() => {
407+
sub.unsubscribe();
408+
resolve();
409+
});
410+
});
411+
}
412+
395413
public async stop(): Promise<void> {
396414
if (this.starting) throw new Error("p2pool is starting");
397415
if (!this.running) throw new Error("Already stopped p2pool");

src/app/core/services/xmrig/xmrig.service.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { EventEmitter, Injectable } from '@angular/core';
1+
import { EventEmitter, inject, Injectable } from '@angular/core';
22
import { MiningStatus, XmrigSettings } from '../../../../common';
33
import { IDBPDatabase, openDB } from 'idb';
4+
import { P2poolService } from '../p2pool/p2pool.service';
45

56
type Status = 'running' | 'stopped' | 'starting' | 'restarting' | 'stopping';
67

@@ -16,6 +17,8 @@ export class XmrigService {
1617

1718
// #region Attributes
1819

20+
private readonly p2poolService = inject(P2poolService);
21+
1922
private readonly openDbPromise: Promise<IDBPDatabase>;
2023
private readonly dbName = 'XmrigSettingsDB';
2124
private readonly storeName = 'settingsStore';
@@ -149,18 +152,22 @@ export class XmrigService {
149152
this._logs = [];
150153
}
151154

152-
public async refreshMiningStatus(networkDifficulty: number): Promise<void> {
153-
const _config = this._settings;
154-
155-
await new Promise<void>((resolve, reject) => {
156-
window.electronAPI.xmrigCmd('h', (result: { error?: string}) => {
155+
private async sendCommand(cmd: 'h' | 'p' | 'r' | 'c'): Promise<void> {
156+
return await new Promise<void>((resolve, reject) => {
157+
window.electronAPI.xmrigCmd(cmd, (result: { error?: string}) => {
157158
if (result.error) reject(new Error(result.error));
158159
else resolve();
159160
});
160161
});
162+
}
163+
164+
public async refreshMiningStatus(networkDifficulty: number): Promise<void> {
165+
const _config = this._settings;
166+
167+
await this.sendCommand('h');
161168

162169
const lastHash = this._lastHashStatus;
163-
const info = lastHash.split('\n')[4]
170+
const info = lastHash.split('\n').find((line) => line.includes('speed'));
164171
if (info) {
165172
const c = info.split(' ').filter(x => x !== '');
166173
const hash10s = c[5];
@@ -194,7 +201,7 @@ export class XmrigService {
194201
'');
195202
}
196203

197-
public async start(config?: XmrigSettings): Promise<void> {
204+
public async start(config?: XmrigSettings, waitForP2Pool: boolean = false): Promise<void> {
198205
const _config = config ? config : this._settings;
199206
if (_config.path === '') throw new Error("Xmrig not configured. Go to Settings -> Mining");
200207
if (this.running) throw new Error("Already running xmrig");
@@ -204,12 +211,14 @@ export class XmrigService {
204211
this._status = 'starting';
205212

206213
try {
214+
if (waitForP2Pool) await this.p2poolService.waitForSidechainSync();
215+
207216
const promise = new Promise<void>((resolve, reject) => {
208217

209218
window.electronAPI.onXmrigOutput(({stdout, stderr} : { stdout?: string, stderr?: string }) => {
210219
if (stdout) {
211220
stdout = this.cleanLog(stdout);
212-
if (stdout.includes("CPU") && stdout.includes("AFFINITY")) {
221+
if (stdout.includes("speed")) {
213222
this._lastHashStatus = stdout;
214223
} else {
215224
this._logs.push(stdout);
@@ -226,6 +235,9 @@ export class XmrigService {
226235
console.log(code);
227236
this._status = 'stopped';
228237
this._startedAt = 0;
238+
this._lastHash10s = 0;
239+
this._lastHash60s = 0;
240+
this._lastHash15m = 0;
229241
this.onStop.emit();
230242
});
231243

src/app/pages/mining/mining.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,14 @@ <h4 class="mb-3">Start mining</h4>
201201

202202
<div class="form-check form-switch col-md-4">
203203
<label for="start-mining-do-background-mining" class="form-check-label">Do Background Mining</label>
204-
<input class="form-control form-check-input" type="checkbox" role="switch" id="start-mining-do-background-mining" [disabled]="startMiningType !== 'solo-mining'" [checked]="startMiningDoBackgroundMining && startMiningType === 'solo-mining'" [(ngModel)]="startMiningDoBackgroundMining" [ngModelOptions]="{standalone: true}">
204+
<input class="form-control form-check-input" type="checkbox" role="switch" id="start-mining-do-background-mining" [disabled]="startMiningType !== 'solo-mining' && !xmrigInstalled" [checked]="startMiningDoBackgroundMining && startMiningType === 'solo-mining'" [(ngModel)]="startMiningDoBackgroundMining" [ngModelOptions]="{standalone: true}">
205205
<br>
206206
<small class="text-body-secondary">States if the mining should run in background (true) or foreground (false)</small>
207207
</div>
208208

209209
<div class="form-check form-switch col-md-4">
210210
<label for="start-mining-ignore-battery" class="form-check-label">Ignore Battery</label>
211-
<input class="form-control form-check-input" type="checkbox" role="switch" id="start-mining-ignore-battery" [disabled]="startMiningType !== 'solo-mining'" [checked]="startMiningIgnoreBattery && startMiningType === 'solo-mining'" [(ngModel)]="startMiningIgnoreBattery" [ngModelOptions]="{standalone: true}">
211+
<input class="form-control form-check-input" type="checkbox" role="switch" id="start-mining-ignore-battery" [disabled]="startMiningType !== 'solo-mining' && !xmrigInstalled" [checked]="startMiningIgnoreBattery && startMiningType === 'solo-mining'" [(ngModel)]="startMiningIgnoreBattery" [ngModelOptions]="{standalone: true}">
212212
<br>
213213
<small class="text-body-secondary">States if battery state (on laptop) should be ignored (true) or not (false)</small>
214214
</div>

src/app/pages/mining/mining.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,8 @@ export class MiningComponent extends BasePageComponent implements AfterViewInit,
971971
}
972972

973973
p2poolConf.wallet = this.startMiningMinerAddress;
974+
xmrigConf.url = '127.0.0.1:3333';
975+
xmrigConf.daemon = false;
974976
xmrigConf.user = this.startMiningMinerAddress;
975977
xmrigConf.background = this.startMiningDoBackgroundMining;
976978
xmrigConf.pauseOnBattery = this.startMiningIgnoreBattery === false;
@@ -979,7 +981,7 @@ export class MiningComponent extends BasePageComponent implements AfterViewInit,
979981

980982
try {
981983
await this.p2poolService.start(p2poolConf);
982-
if (xmrigInstalled) await this.xmrigService.start(xmrigConf);
984+
if (xmrigInstalled) await this.xmrigService.start(xmrigConf, true);
983985
await this.daemonData.refreshMiningStatus();
984986
} catch (error: any) {
985987
err = error;
@@ -1001,6 +1003,7 @@ export class MiningComponent extends BasePageComponent implements AfterViewInit,
10011003
if (this.xmrigInstalled) {
10021004
const xmrigConf = XmrigSettings.fromDaemonSettings(conf);
10031005

1006+
xmrigConf.url = '';
10041007
xmrigConf.user = this.startMiningMinerAddress;
10051008
xmrigConf.coin = 'monero';
10061009
xmrigConf.daemon = true;

src/common/XmrigSettings.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export class XmrigSettings extends Comparable<XmrigSettings> {
1414
public daemonZmqPort: number = 0;
1515
public background: boolean = false;
1616
public pauseOnBattery: boolean = false;
17+
public noColor: boolean = true;
1718

1819
public override clone(): XmrigSettings {
1920
const result = Object.assign(new XmrigSettings(), this);
@@ -29,13 +30,15 @@ export class XmrigSettings extends Comparable<XmrigSettings> {
2930
if (this.threads) cmdList.push(`--threads=${this.threads}`);
3031
if (this.rigId) cmdList.push(`--rig-id=${this.rigId}`);
3132
if (this.url) cmdList.push(`--url=${this.url}`);
32-
if (this.httpHost) cmdList.push(`--http-host=${this.httpHost}`);
33-
if (this.httpPort > 0) cmdList.push(`--http-port=${this.httpPort}`);
33+
//if (this.httpHost) cmdList.push(`--http-host=${this.httpHost}`);
34+
//if (this.httpPort > 0) cmdList.push(`--http-port=${this.httpPort}`);
3435
if (this.daemon) cmdList.push('--daemon');
3536
if (this.daemonZmqPort > 0) cmdList.push(`--daemon-zmq-port=${this.daemonZmqPort}`);
3637
if (this.coin) cmdList.push(`--coin=${this.coin}`);
3738
if (this.background) cmdList.push(`--background`);
3839
if (this.pauseOnBattery) cmdList.push(`--pause-on-battery`);
40+
if (this.noColor) cmdList.push(`--no-color`);
41+
3942
return cmdList;
4043
}
4144

0 commit comments

Comments
 (0)