Settings implementation

This commit is contained in:
everoddandeven 2024-09-22 15:55:03 +02:00
parent 334a124a4a
commit 80e8a283f4
61 changed files with 1307 additions and 104 deletions

View file

@ -36,16 +36,16 @@
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss",
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"node_modules/bootstrap-table/dist/bootstrap-table.min.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"node_modules/bootstrap-table/dist/bootstrap-table.js"
],
"styles": [
"src/styles.scss",
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"node_modules/bootstrap-table/dist/bootstrap-table.min.css"
],
"customWebpackConfig": {
"path": "./angular.webpack.js",
"replaceDuplicatePlugins": true

View file

@ -1,7 +1,11 @@
import {app, BrowserWindow, screen} from 'electron';
import {app, BrowserWindow, ipcMain, screen} from 'electron';
import { ChildProcess, ChildProcessWithoutNullStreams, exec, spawn } from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
const monerodFilePath: string = "/home/sidney/Documenti/monero-x86_64-linux-gnu-v0.18.3.4/monerod";
let win: BrowserWindow | null = null;
const args = process.argv.slice(1),
serve = args.some(val => val === '--serve');
@ -19,7 +23,7 @@ function createWindow(): BrowserWindow {
webPreferences: {
nodeIntegration: true,
allowRunningInsecureContent: (serve),
contextIsolation: false,
contextIsolation: false
},
});
@ -53,6 +57,81 @@ function createWindow(): BrowserWindow {
return win;
}
function execMoneroDaemon(configFilePath: string): ChildProcess {
const monerodPath = path.resolve(__dirname, 'path/to/monerod'); // Percorso del binario di monerod
//const command = `"${monerodPath}" --config-file "${configFilePath}"`;
const command = `/home/sidney/Documenti/monero-x86_64-linux-gnu-v0.18.3.4/monerod --testnet --fast-block-sync 1 --prune-blockchain --sync-pruned-blocks --confirm-external-bind --max-concurrency 1 --log-level 1 --rpc-access-control-origins=*`;
const monerodProcess = exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Errore durante l'avvio di monerod: ${error.message}`);
return;
}
if (stderr) {
console.error(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
// Gestisci l'output in tempo reale
if (monerodProcess.stdout == null) {
throw new Error("No stdout for monero process")
}
if (monerodProcess.stderr == null) {
throw new Error("No stderr for monero process");
}
monerodProcess.stdout.on('data', (data) => {
console.log(`monerod stdout: ${data}`);
});
monerodProcess.stderr.on('data', (data) => {
console.error(`monerod stderr: ${data}`);
});
return monerodProcess;
}
function startMoneroDaemon(configFilePath: string): ChildProcessWithoutNullStreams {
const monerodPath = path.resolve(__dirname, monerodFilePath);
const args = [
'--testnet',
'--fast-block-sync', '1',
'--prune-blockchain',
'--sync-pruned-blocks',
'--confirm-external-bind',
'--max-concurrency', '1',
'--log-level', '1',
'--rpc-access-control-origins=*'
];
// Avvia il processo usando spawn
const monerodProcess = spawn(monerodPath, args);
// Gestisci l'output di stdout in streaming
monerodProcess.stdout.on('data', (data) => {
console.log(`monerod stdout: ${data}`);
// Puoi anche inviare i log all'interfaccia utente tramite IPC
});
// Gestisci gli errori in stderr
monerodProcess.stderr.on('data', (data) => {
console.error(`monerod stderr: ${data}`);
});
// Gestisci la chiusura del processo
monerodProcess.on('close', (code) => {
console.log(`monerod chiuso con codice: ${code}`);
});
return monerodProcess;
}
try {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
@ -77,6 +156,10 @@ try {
}
});
ipcMain.on('start-monerod', (event, configFilePath) => {
startMoneroDaemon(configFilePath);
})
} catch (e) {
// Catch Error
// throw e;

View file

@ -5,6 +5,7 @@ import { PageNotFoundComponent } from './shared/components';
import { HomeRoutingModule } from './home/home-routing.module';
import { DetailRoutingModule } from './detail/detail-routing.module';
import { HardForkInfoRoutingModule } from './hard-fork-info/hard-fork-info-routing.module';
import { SettingsModule } from './settings/settings.module';
const routes: Routes = [
{
@ -23,7 +24,8 @@ const routes: Routes = [
RouterModule.forRoot(routes, {}),
HomeRoutingModule,
DetailRoutingModule,
HardForkInfoRoutingModule
HardForkInfoRoutingModule,
SettingsModule
],
exports: [RouterModule]
})

View file

@ -16,6 +16,7 @@ import { DetailModule } from './detail/detail.module';
import { AppComponent } from './app.component';
import { SidebarComponent } from "./sidebar/sidebar.component";
import { LoadComponent } from "./load/load.component";
import { BansModule } from './bans/bans.module';
import { NavbarComponent } from "./navbar/navbar.component";
import { MiningModule } from './mining/mining.module';
@ -44,7 +45,8 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
}
}),
SidebarComponent,
NavbarComponent
NavbarComponent,
LoadComponent
],
providers: [],
bootstrap: [AppComponent]

View file

@ -20,17 +20,19 @@ export class BansComponent implements AfterViewInit {
}
ngAfterViewInit(): void {
console.log('BansComponent AFTER VIEW INIT');
this.navbarService.removeNavbarLinks();
setTimeout(() => {
const $table = $('#bansTable');
$table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
this.load();
}, 500);
console.log('BansComponent AFTER VIEW INIT');
setTimeout(() => {
const $table = $('#bansTable');
$table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
this.load();
}, 500);
}
private onNavigationEnd(): void {

View file

@ -16,7 +16,8 @@ import {
PruneBlockchainRequest,
CalculatePoWHashRequest,
FlushCacheRequest,
GetMinerDataRequest
GetMinerDataRequest,
EmptyRpcRequest
} from '../../../../common/request';
import { BlockTemplate } from '../../../../common/BlockTemplate';
import { GeneratedBlocks } from '../../../../common/GeneratedBlocks';
@ -34,23 +35,65 @@ import { RelayTxRequest } from '../../../../common/request/RelayTxRequest';
import { TxBacklogEntry } from '../../../../common/TxBacklogEntry';
import { BlockchainPruneInfo } from '../../../../common/BlockchainPruneInfo';
import { MinerData } from '../../../../common/MinerData';
import { CoreIsBusyError } from '../../../../common/error';
import { ElectronService } from '../electron/electron.service';
@Injectable({
providedIn: 'root'
})
export class DaemonService {
private readonly configFilePath: string = './config';
private url: string = "http://127.0.0.1:28081";
//private url: string = "http://node2.monerodevs.org:28089";
//private url: string = "https://testnet.xmr.ditatompel.com";
//private url: string = "https://xmr.yemekyedim.com:18081";
//private url: string = "https://moneronode.org:18081";
private readonly headers: { [key: string]: string } = {
'Content-Type': 'application/json'
"Access-Control-Allow-Headers": "*", // this will allow all CORS requests
"Access-Control-Allow-Methods": 'POST,GET' // this states the allowed methods
};
constructor(private httpClient: HttpClient) { }
constructor(private httpClient: HttpClient, private electronService: ElectronService) { }
private async callJsonRpc(params: JsonRPCRequest): Promise<{ [key: string]: any }> {
return await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${this.url}/json_rpc`, params.toDictionary(), this.headers));
}
public async startDaemon(): Promise<void> {
if (await this.isRunning()) {
console.warn("Daemon already running");
return;
}
if (!this.electronService.isElectron) {
console.error("Could not start monero daemon: not electron app");
return;
}
console.log("Starting daemon");
this.electronService.ipcRenderer.send('start-monerod', this.configFilePath);
console.log("Daemon started");
setTimeout(() => {
}, 500)
}
public async isRunning(): Promise<boolean> {
try {
const response = await this.callJsonRpc(new EmptyRpcRequest());
console.log(response);
return true;
}
catch(error) {
console.error(error);
return false;
}
}
public async getBlockCount(): Promise<BlockCount> {
const response = await this.callJsonRpc(new GetBlockCountRequest());
@ -147,10 +190,15 @@ export class DaemonService {
public async getBans(): Promise<Ban[]> {
const response = await this.callJsonRpc(new GetBansRequest());
if (response.error) {
this.raiseRpcError(response.error);
}
const bans: any[] = response.bans;
const result: Ban[] = [];
bans.forEach((ban: any) => result.push(Ban.parse(ban)));
if (bans) bans.forEach((ban: any) => result.push(Ban.parse(ban)));
return result;
}
@ -249,8 +297,24 @@ export class DaemonService {
public async getMinerData(): Promise<MinerData> {
const response = await this.callJsonRpc(new GetMinerDataRequest());
if (response.error) {
this.raiseRpcError(response.error);
}
return MinerData.parse(response.result);
}
private raiseRpcError(error: { code: number, message: string }): void {
if (error.code == -9) {
throw new CoreIsBusyError();
}
else
{
throw new Error(error.message);
}
}
}

View file

@ -1,7 +1,15 @@
<div class="tab-content" id="pills-tabContent">
<app-load [show]="loading"></app-load>
<div class="tab-content" id="pills-tabContent" [hidden]="loading">
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" tabindex="0">
<div class="row d-flex">
<div *ngIf="!daemonRunning" class="h-100 p-5 text-bg-dark rounded-3 m-4">
<h2>Daemon not running</h2>
<p>Start monero daemon</p>
<button class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button>
</div>
@for(card of cards; track card.header) {
<div class="card text-bg-dark m-3 text-center" style="max-width: 18rem;">
<div class="card-header">{{card.header}}</div>

View file

@ -1,7 +1,5 @@
import { Component, OnInit, AfterViewInit } from '@angular/core';
import { Component, OnInit, AfterViewInit, NgZone } from '@angular/core';
import { DaemonService } from '../core/services/daemon/daemon.service';
import * as jquery from 'jquery';
import * as bootstrapTable from 'bootstrap-table'
import { SyncInfo } from '../../common/SyncInfo';
import { Peer } from '../../common/Peer';
import { NavbarLink } from '../navbar/navbar.model';
@ -16,6 +14,7 @@ import { DaemonInfo } from '../../common/DaemonInfo';
})
export class DetailComponent implements OnInit, AfterViewInit {
public daemonRunning: boolean;
private syncInfo?: SyncInfo;
private daemonInfo?: DaemonInfo;
private readonly navbarLinks: NavbarLink[];
@ -37,9 +36,16 @@ export class DetailComponent implements OnInit, AfterViewInit {
private nodeType: string;
private syncProgress: string;
private isLoading: boolean;
public get loading(): boolean {
return this.isLoading;
}
public cards: Card[];
constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService) {
constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
this.daemonRunning = false;
this.syncStatus = 'Not synced';
this.height = 0;
this.targetHeight = 0;
@ -55,6 +61,7 @@ export class DetailComponent implements OnInit, AfterViewInit {
this.nodeType = 'unknown';
this.blockchainSize = '0 GB';
this.syncProgress = '0 %';
this.isLoading = true;
this.navbarLinks = [
new NavbarLink('pills-home-tab', '#pills-home', 'pills-home', true, 'Overview'),
@ -77,27 +84,42 @@ export class DetailComponent implements OnInit, AfterViewInit {
ngAfterViewInit(): void {
console.log('DetailComponent AFTER VIEW INIT');
this.navbarService.setNavbarLinks(this.navbarLinks);
setTimeout(() => {
const $table = $('#table');
$table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
this.Load();
this.ngZone.run(() => {
const $table = $('#table');
$table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
this.load();
}, 500);
});
}
}, 500);
public async startDaemon(): Promise<void> {
if (this.daemonRunning) {
console.warn("Daemon already running");
return;
}
await this.daemonService.startDaemon();
this.daemonRunning = await this.daemonService.isRunning();
}
private onNavigationEnd(): void {
this.Load().then(() => {
this.load().then(() => {
this.cards = this.createCards();
this.navbarService.setNavbarLinks(this.navbarLinks);
});
}
private createCards(): Card[] {
if (!this.daemonRunning) {
return []
}
return [
new Card('Connection Status', this.connectionStatus),
new Card('Network Type', this.networkType),
@ -114,50 +136,64 @@ export class DetailComponent implements OnInit, AfterViewInit {
];
}
public async Load(): Promise<void> {
const $table = $('#table');
private async load(): Promise<void> {
try {
this.isLoading = true;
this.daemonRunning = await this.daemonService.isRunning();
if (!this.daemonRunning) {
return;
}
const $table = $('#table');
this.syncInfo = await this.daemonService.syncInfo();
this.height = this.syncInfo.height;
this.targetHeight = this.syncInfo.targetHeight;
this.nextNeededPruningSeed = this.syncInfo.nextNeededPruningSeed;
if (this.height > 0 && this.targetHeight == 0) {
this.targetHeight = this.height;
this.syncStatus = 'Daemon synced';
}
else if (this.height > 0 && this.targetHeight > 0 && this.height == this.targetHeight) {
this.syncStatus = 'Daemon synced';
}
this.overview = this.syncInfo.overview;
const blockCount = await this.daemonService.getBlockCount();
this.blockCount = blockCount.count;
const version = await this.daemonService.getVersion();
this.version = `${version.version}`;
this.daemonInfo = await this.daemonService.getInfo();
const capacity: number = this.daemonInfo.freeSpace + this.daemonInfo.databaseSize;
const diskUsage = parseInt(`${this.daemonInfo.databaseSize * 100 / capacity}`);
const blockchainSize = (this.daemonInfo.databaseSize / 1000 / 1000 / 1000).toFixed(2);
this.blockchainSize = `${blockchainSize} GB`;
this.diskUsage = `${diskUsage} %`;
this.networkType = this.daemonInfo.nettype;
this.connectionStatus = this.daemonInfo.offline ? 'offline' : 'online';
this.txCount = this.daemonInfo.txCount;
this.poolSize = this.daemonInfo.txPoolSize;
this.version = this.daemonInfo.version;
this.syncProgress = `${(this.height*100/this.targetHeight).toFixed(2)} %`;
//const blockchainPruned = await this.isBlockchainPruned();
const blockchainPruned = false;
this.nodeType = blockchainPruned ? 'pruned' : 'full';
$table.bootstrapTable('load', this.getPeers());
}
catch(error) {
console.error(error);
}
this.syncInfo = await this.daemonService.syncInfo();
this.height = this.syncInfo.height;
this.targetHeight = this.syncInfo.targetHeight;
this.nextNeededPruningSeed = this.syncInfo.nextNeededPruningSeed;
if (this.height > 0 && this.targetHeight == 0) {
this.targetHeight = this.height;
this.syncStatus = 'Daemon synced';
}
else if (this.height > 0 && this.targetHeight > 0 && this.height == this.targetHeight) {
this.syncStatus = 'Daemon synced';
}
this.overview = this.syncInfo.overview;
const blockCount = await this.daemonService.getBlockCount();
this.blockCount = blockCount.count;
const version = await this.daemonService.getVersion();
this.version = `${version.version}`;
this.daemonInfo = await this.daemonService.getInfo();
const capacity: number = this.daemonInfo.freeSpace + this.daemonInfo.databaseSize;
const diskUsage = parseInt(`${this.daemonInfo.databaseSize * 100 / capacity}`);
const blockchainSize = (this.daemonInfo.databaseSize / 1000 / 1000 / 1000).toFixed(2);
this.blockchainSize = `${blockchainSize} GB`;
this.diskUsage = `${diskUsage} %`;
this.networkType = this.daemonInfo.nettype;
this.connectionStatus = this.daemonInfo.offline ? 'offline' : 'online';
this.txCount = this.daemonInfo.txCount;
this.poolSize = this.daemonInfo.txPoolSize;
this.version = this.daemonInfo.version;
this.syncProgress = `${(this.height*100/this.targetHeight).toFixed(2)} %`;
//const blockchainPruned = await this.isBlockchainPruned();
const blockchainPruned = false;
this.nodeType = blockchainPruned ? 'pruned' : 'full';
$table.bootstrapTable('load', this.getPeers());
this.isLoading = false;
}
public async isBlockchainPruned(): Promise<boolean> {

View file

@ -5,9 +5,10 @@ import { DetailRoutingModule } from './detail-routing.module';
import { DetailComponent } from './detail.component';
import { SharedModule } from '../shared/shared.module';
import { LoadComponent } from "../load/load.component";
@NgModule({
declarations: [DetailComponent],
imports: [CommonModule, SharedModule, DetailRoutingModule]
imports: [CommonModule, SharedModule, DetailRoutingModule, LoadComponent]
})
export class DetailModule {}

View file

@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { AfterViewInit, Component } from '@angular/core';
import { DaemonService } from '../core/services/daemon/daemon.service';
import { NavigationEnd, Router } from '@angular/router';
import { NavbarService } from '../navbar/navbar.service';
@ -8,7 +8,7 @@ import { NavbarService } from '../navbar/navbar.service';
templateUrl: './hard-fork-info.component.html',
styleUrl: './hard-fork-info.component.scss'
})
export class HardForkInfoComponent {
export class HardForkInfoComponent implements AfterViewInit {
public cards: Card[];
private earliestHeight: number;
private enabled: boolean;
@ -36,10 +36,13 @@ export class HardForkInfoComponent {
});
}
ngAfterViewInit(): void {
this.navbarService.removeNavbarLinks();
}
private onNavigationEnd(): void {
this.load().then(() => {
this.cards = this.createCards();
this.navbarService.removeNavbarLinks();
});
}

View file

@ -0,0 +1,9 @@
<div *ngIf="show" class="row g-5 m-2">
<div class="col-md-7 col-lg-10 flex-fill">
<div class="d-flex justify-content-center m-5">
<div class="spinner-border" style="width: 3rem; height: 3rem;" role="status">
<span class="sr-only"></span>
</div>
</div>
</div>
</div>

View file

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoadComponent } from './load.component';
describe('LoadComponent', () => {
let component: LoadComponent;
let fixture: ComponentFixture<LoadComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoadComponent]
})
.compileComponents();
fixture = TestBed.createComponent(LoadComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,15 @@
import { NgIf } from '@angular/common';
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-load',
standalone: true,
imports: [NgIf],
templateUrl: './load.component.html',
styleUrl: './load.component.scss'
})
export class LoadComponent {
@Input() public show: boolean = false;
}

View file

@ -6,6 +6,7 @@ import { NavigationEnd, Router } from '@angular/router';
import { NavbarLink } from '../navbar/navbar.model';
import { MineableTxBacklog } from '../../common/MineableTxBacklog';
import { Chain } from '../../common/Chain';
import { CoreIsBusyError } from '../../common/error';
@Component({
selector: 'app-mining',
@ -15,7 +16,7 @@ import { Chain } from '../../common/Chain';
export class MiningComponent implements AfterViewInit {
private readonly navbarLinks: NavbarLink[];
private coreBusy: boolean;
private minerData?: MinerData;
private majorVersion: number;
@ -40,6 +41,7 @@ export class MiningComponent implements AfterViewInit {
this.alreadyGeneratedCoins = 0;
this.alternateChains = [];
this.cards = [];
this.coreBusy = false;
this.navbarLinks = [
new NavbarLink('pills-miner-data-tab', '#pills-miner-data', 'miner-data', true, 'Miner Data'),
@ -57,6 +59,7 @@ export class MiningComponent implements AfterViewInit {
ngAfterViewInit(): void {
console.log('DetailComponent AFTER VIEW INIT');
this.navbarService.setNavbarLinks(this.navbarLinks);
setTimeout(() => {
const $table = $('#chainsTable');
@ -72,28 +75,39 @@ export class MiningComponent implements AfterViewInit {
private onNavigationEnd(): void {
this.load().then(() => {
this.cards = this.createCards();
this.navbarService.setNavbarLinks(this.navbarLinks)
});
}
private async load(): Promise<void> {
this.minerData = await this.daemonService.getMinerData();
try {
this.minerData = await this.daemonService.getMinerData();
this.majorVersion = this.minerData.majorVersion;
this.height = this.minerData.height;
this.prevId = this.minerData.prevId;
this.seedHash = this.minerData.seedHash;
this.difficulty = this.minerData.difficulty;
this.medianWeight = this.minerData.medianWeight;
this.alreadyGeneratedCoins = this.minerData.alreadyGeneratedCoins;
this.majorVersion = this.minerData.majorVersion;
this.height = this.minerData.height;
this.prevId = this.minerData.prevId;
this.seedHash = this.minerData.seedHash;
this.difficulty = this.minerData.difficulty;
this.medianWeight = this.minerData.medianWeight;
this.alreadyGeneratedCoins = this.minerData.alreadyGeneratedCoins;
this.alternateChains = await this.daemonService.getAlternateChains();
this.alternateChains = await this.daemonService.getAlternateChains();
const $table = $('#chainsTable');
$table.bootstrapTable('load', this.getChains());
const $table = $('#chainsTable');
$table.bootstrapTable('load', this.getChains());
}
catch(error) {
if (error instanceof CoreIsBusyError) {
this.coreBusy = true;
}
}
}
private createCards(): Card[] {
if (this.coreBusy) {
return [
new Card('Error', 'Core is busy')
]
}
return [
new Card('Major Fork Version', `${this.majorVersion}`),
new Card('Current block height', `${this.height}`),

View file

@ -6,7 +6,7 @@
<ul class="nav nav-pills" id="pills-tab" role="tablist">
@for(navbarLink of navbarLinks; track navbarLink.name) {
<li class="nav-item" role="presentation">
<li class="nav-item mr-2" role="presentation">
<button [class]="navbarLink.selected ? 'nav-link active' : 'nav-link'" [id]="navbarLink.id" data-bs-toggle="pill" [attr.data-bs-target]="navbarLink.target" type="button" role="tab" [attr.aria-controls]="navbarLink.controls" [attr.aria-selected]="navbarLink.selected">{{navbarLink.name}}</button>
</li>
}

View file

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SettingsComponent } from './settings.component';
const routes: Routes = [
{
path: 'settings',
component: SettingsComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SettingsRoutingModule { }

View file

@ -0,0 +1,380 @@
<div class="tab-content" id="pills-settings-tabContent">
<div class="tab-pane fade show active" id="pills-rpc" role="tabpanel" aria-labelledby="pills-rpc-tab" tabindex="0">
<div class="row g-5 m-2">
<div class="col-md-7 col-lg-10">
<h4 class="mb-3">General</h4>
<div class="row gy-3">
<div class="form-check form-switch col-md-6">
<label for="offline" class="form-check-label">Offline</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="offline">
<br>
<small class="text-body-secondary">Do not listen for peers, nor connect to any</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="public-node" class="form-check-label">Public node</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="public-node">
<br>
<small class="text-body-secondary">Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="restricted-rpc" class="form-check-label">Restricted RPC</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="restricted-rpc">
<br>
<small class="text-body-secondary">Restrict RPC to view-only commands and do not return privacy sensitive data in RPC calls</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="confirm-external-bind" class="form-check-label">Confirm external bind</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="confirm-external-bind">
<br>
<small class="text-body-secondary">Confirm Bind IP is not a loopback (local) IP</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="rpc-ignore-ipv4" class="form-check-label">Ignore IPv4</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ignore-ipv4">
<br>
<small class="text-body-secondary">Ignore unsuccessful IPv4 bind for RPC</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="disable-rpc-ban" class="form-check-label">Disable ban</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="disable-rpc-ban">
<br>
<small class="text-body-secondary">Do not ban hosts on RPC errors</small>
</div>
<div class="col-md-6">
<label for="rpc-bind-ip" class="form-label">Bind IP</label>
<input type="text" class="form-control" id="rpc-bind-ip" placeholder="127.0.0.1">
<small class="text-body-secondary">Specify IP to bind RPC server</small>
</div>
<div class="col-md-6">
<label for="rpc-bind-port" class="form-label">Bind port</label>
<input type="number" class="form-control" id="rpc-bind-port" placeholder="18081">
<small class="text-body-secondary">18081 for mainnet, 28081 for testnet, 38081 for stagenet</small>
</div>
<div class="col-sm-6">
<label for="rpc-login-user" class="form-label">Username</label>
<input type="text" class="form-control" id="rpc-login-user" placeholder="" value="">
<small class="text-body-secondary">Specify username required for RPC server</small>
</div>
<div class="col-sm-6">
<label for="rpc-login-password" class="form-label">Password</label>
<input type="password" class="form-control" id="rpc-login-password" placeholder="" value="">
<small class="text-body-secondary">Specify password required for RPC server</small>
</div>
<div class="col-sm-12">
<label for="rpc-access-control-origins" class="form-label">Access control origins</label>
<input type="text" class="form-control" id="rpc-access-control-origins" placeholder="" value="">
<small class="text-body-secondary">Specify a comma separated list of origins to allow cross origin resource sharing</small>
</div>
</div>
<hr class="my-4">
<div class="row g-3">
<h4 class="mb-3">IPv6</h4>
<div class="form-check form-switch col-md-12">
<label for="rpc-use-ipv6" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-use-ipv6">
<br>
<small class="text-body-secondary">Allow IPv6 for RPC</small>
</div>
<div class="col-md-6">
<label for="rpc-bind-ipv6-address" class="form-label">Bind IPv6 address</label>
<input type="text" class="form-control" id="rpc-bind-ipv6-address" placeholder="::1">
<small class="text-body-secondary">Specify IPv6 address to bind RPC server</small>
</div>
<div class="col-md-6">
<label for="rpc-restricted-bind-ipv6-address" class="form-label">Restricted bind IPv6 address</label>
<input type="text" class="form-control" id="rpc-restricted-bind-ipv6-address" placeholder="::1">
<small class="text-body-secondary">Specify IPv6 address to bind restricted RPC server</small>
</div>
</div>
<hr class="my-4">
<div class="row g-3">
<h4 class="mb-3">ZMQ</h4>
<div class="form-check form-switch col-md-12">
<label for="enable-zmq" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="enable-zmq">
<br>
<small class="text-body-secondary">Enable ZMQ RPC Server</small>
</div>
<div class="col-md-6">
<label for="zmq-rpc-bind-ip" class="form-label">Bind IP</label>
<input type="text" class="form-control" id="zmq-rpc-bind-ip" placeholder="127.0.0.1">
<small class="text-body-secondary">IP for ZMQ RPC Server to listen on</small>
</div>
<div class="col-md-6">
<label for="zmq-rpc-bind-port" class="form-label">Bind port</label>
<input type="number" class="form-control" id="zmq-rpc-bind-port" placeholder="18082">
<small class="text-body-secondary">18082 for mainnet, 28082 for testnet, 38082 for stagenet</small>
</div>
<div class="col-md-6">
<label for="zmq-pub" class="form-label">ZMQ Pub</label>
<input type="text" class="form-control" id="zmq-pub" placeholder="tcp://ip:port or ipc://path">
<small class="text-body-secondary">Address for ZMQ Pub</small>
</div>
</div>
<hr class="my-4">
<div class="row g-3">
<h4 class="mb-3">Payment</h4>
<div class="col-md-12">
<label for="rpc-payment-address" class="form-label">Address</label>
<input type="text" class="form-control" id="rpc-payment-address">
<small class="text-body-secondary">Restrict RPC to clients sending micropayment to this address</small>
</div>
<div class="col-md-6">
<label for="rpc-payment-difficulty" class="form-label">Difficulty</label>
<input type="number" class="form-control" id="rpc-payment-difficulty" placeholder="1000">
<small class="text-body-secondary">Restrict RPC to clients sending micropayment at this difficulty</small>
</div>
<div class="col-md-6">
<label for="rpc-payment-credits" class="form-label">Credits</label>
<input type="number" class="form-control" id="rpc-payment-credits" placeholder="100">
<small class="text-body-secondary">Restrict RPC to clients sending micropayment, yelds that many credits per payment</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="rpc-payment-allow-free-loopback" class="form-check-label">Allow free loopback</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-payment-allow-free-loopback">
<br>
<small class="text-body-secondary">Allow free access from the loopback address (ie, the local host)</small>
</div>
</div>
<hr class="my-4">
<div class="row g-3">
<h4 class="mb-3">SSL</h4>
<div class="col-md-4">
<label for="rpc-ssl" class="form-label">SSL Mode</label>
<select class="form-select" id="rpc-ssl">
<option value="autodetect">Autodetect</option>
<option value="enabled">Enabled</option>
<option value="disabled">Disabled</option>
</select>
</div>
<div class="form-check form-switch col-md-4">
<label for="rpc-ssl-allow-chained" class="form-check-label">Allow chained</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ssl-allow-chained">
<br>
<small class="text-body-secondary">Allow user chain certificates</small>
</div>
<div class="form-check form-switch col-md-4">
<label for="rpc-ssl-allow-any-cert" class="form-check-label">Allow any cert</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ssl-allow-any-cert">
<br>
<small class="text-body-secondary">Allow any peer certificate</small>
</div>
<div class="col-md-12">
<label for="rpc-ssl-private-key" class="form-label">Private key</label>
<input type="file" class="form-control" id="rpc-ssl-private-key">
<small class="text-body-secondary">Path to a PEM format private key</small>
</div>
<div class="col-md-12">
<label for="rpc-ssl-certificate" class="form-label">Certificate</label>
<input type="file" class="form-control" id="rpc-ssl-certificate">
<small class="text-body-secondary">Path to a PEM format certificate</small>
</div>
<div class="col-md-12">
<label for="rpc-ssl-ca-certificates" class="form-label">CA Certificates</label>
<input type="file" class="form-control" id="rpc-ssl-ca-certificates">
<small class="text-body-secondary">Path to file containing concatenated PEM format certificate(s) to replace system CA(s)</small>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-blockchain" role="tabpanel" aria-labelledby="pills-blockchain-tab" tabindex="0">
<div class="row g-5 m-2">
<div class="col-md-7 col-lg-10">
<h4 class="mb-3">Bootstrap Daemon</h4>
<form class="needs-validation" novalidate="">
<div class="row g-3">
<div class="col-12">
<label for="address" class="form-label">Address</label>
<input type="text" class="form-control" id="address" placeholder="Use 'auto' to enable automatic discovering and switching">
<small class="text-body-secondary">URL of a bootstrap remote daemon that the connected wallets can use while this daemon is still not fully synced.</small>
</div>
<div class="col-sm-6">
<label for="firstName" class="form-label">Username</label>
<input type="text" class="form-control" id="firstName" placeholder="" value="">
<small class="text-body-secondary">Specify username for the bootstrap daemon login</small>
</div>
<div class="col-sm-6">
<label for="lastName" class="form-label">Password</label>
<input type="password" class="form-control" id="lastName" placeholder="" value="">
<small class="text-body-secondary">Specify password for the bootstrap daemon login</small>
</div>
<div class="col-12">
<label for="bootstrap-daemon-proxy" class="form-label">Proxy</label>
<input type="text" class="form-control" id="bootstrap-daemon-proxy" placeholder="ip:port">
<small class="text-body-secondary">Socks proxy to use for bootstrap daemon connection</small>
</div>
</div>
<hr class="my-4">
<div class="row g-3">
<h4 class="mb-3">Synchronization</h4>
<div class="form-check form-switch col-md-12">
<label for="sync-enabled" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="sync-enabled" checked>
<br>
<small class="text-body-secondary">Synchronize the blockchain with other peers</small>
</div>
<div class="col-md-4">
<label for="block-sync-size" class="form-label">Block sync size</label>
<input type="number" class="form-control" id="block-sync-size" placeholder="" value="0">
</div>
<div class="col-md-4">
<label for="block-download-max-size" class="form-label">Block download max size</label>
<input type="number" class="form-control" id="block-download-max-size" placeholder="" value="0">
</div>
<div class="col-md-4">
<label for="db-sync-mode" class="form-label">Database sync mode</label>
<input type="text" class="form-control" id="db-sync-mode" placeholder="fast:async:250000000bytes">
</div>
<br>
<br>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="same-address">
<label class="form-check-label" for="same-address">Prune blockchain</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="save-info">
<label class="form-check-label" for="save-info">Sync pruned blocks</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="fast-block-sync">
<label class="form-check-label" for="fast-block-sync">Fast block sync</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="relay-fluffy-blocks" checked>
<label class="form-check-label" for="fast-block-sync">Relay fluffy blocks</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="keep-alt-blocks">
<label class="form-check-label" for="keep-alt-block">Keep alternative blocks on restart</label>
</div>
<hr class="my-4">
<h4 class="mb-3">Network type</h4>
<div class="my-3">
<div class="form-check">
<input id="credit" name="paymentMethod" type="radio" class="form-check-input" checked="" required="">
<label class="form-check-label" for="credit">mainnet</label>
</div>
<div class="form-check">
<input id="debit" name="paymentMethod" type="radio" class="form-check-input" required="">
<label class="form-check-label" for="debit">testnet</label>
</div>
<div class="form-check">
<input id="paypal" name="paymentMethod" type="radio" class="form-check-input" required="">
<label class="form-check-label" for="paypal">stagenet</label>
</div>
</div>
</div>
<!--
<div class="row gy-3">
<div class="col-md-6">
<label for="cc-name" class="form-label">Name on card</label>
<input type="text" class="form-control" id="cc-name" placeholder="" required="">
<small class="text-body-secondary">Full name as displayed on card</small>
<div class="invalid-feedback">
Name on card is required
</div>
</div>
<div class="col-md-6">
<label for="cc-number" class="form-label">Credit card number</label>
<input type="text" class="form-control" id="cc-number" placeholder="" required="">
<div class="invalid-feedback">
Credit card number is required
</div>
</div>
<div class="col-md-3">
<label for="cc-expiration" class="form-label">Expiration</label>
<input type="text" class="form-control" id="cc-expiration" placeholder="" required="">
<div class="invalid-feedback">
Expiration date required
</div>
</div>
<div class="col-md-3">
<label for="cc-cvv" class="form-label">CVV</label>
<input type="text" class="form-control" id="cc-cvv" placeholder="" required="">
<div class="invalid-feedback">
Security code required
</div>
</div>
</div>
-->
<hr class="my-4">
</form>
</div>
</div>
</div>
<hr class="my-4">
<button class="w-50 btn btn-primary btn-lg" type="submit">Save</button>
</div>

View file

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsComponent } from './settings.component';
describe('SettingsComponent', () => {
let component: SettingsComponent;
let fixture: ComponentFixture<SettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SettingsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,257 @@
import { AfterViewInit, Component } from '@angular/core';
import { NavbarService } from '../navbar/navbar.service';
import { NavigationEnd, NavigationStart, Router } from '@angular/router';
import { NavbarLink } from '../navbar/navbar.model';
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrl: './settings.component.scss'
})
export class SettingsComponent implements AfterViewInit {
private readonly navbarLinks: NavbarLink[];
constructor(private router: Router, private navbarService: NavbarService) {
this.navbarLinks = [
new NavbarLink('pills-rpc-tab', '#pills-rpc', 'pills-rpc', true, 'RPC'),
new NavbarLink('pills-p2p-tab', '#pills-p2p', 'pills-p2p', false, 'P2P'),
new NavbarLink('pills-blockchain-tab', '#pills-blockchain', 'pills-blockchain', false, 'Blockchain'),
new NavbarLink('pills-mining-tab', '#pills-mining', 'pills-mining', false, 'Mining'),
new NavbarLink('pills-log-tab', '#pills-log', 'pills-log', false, 'Logs')
];
this.router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
if (event.url != '/settings') return;
this.onNavigationEnd();
}
});
}
ngAfterViewInit(): void {
this.navbarService.setNavbarLinks(this.navbarLinks);
}
private onNavigationEnd(): void {
}
}
/**
* --log-file arg (=/home/sidney/.bitmonero/bitmonero.log, /home/sidney/.bitmonero/testnet/bitmonero.log if 'testnet', /home/sidney/.bitmonero/stagenet/bitmonero.log if 'stagenet')
Specify log file
--log-level arg
--max-log-file-size arg (=104850000) Specify maximum log file size [B]
--max-log-files arg (=50) Specify maximum number of rotated log
files to be saved (no limit by setting
to 0)
--max-concurrency arg (=0) Max number of threads to use for a
parallel job
--proxy arg Network communication through proxy:
<socks-ip:port> i.e. "127.0.0.1:9050"
--proxy-allow-dns-leaks Allow DNS leaks outside of proxy
--public-node Allow other users to use the node as a
remote (restricted RPC mode, view-only
commands) and advertise it over P2P
--zmq-rpc-bind-ip arg (=127.0.0.1) IP for ZMQ RPC server to listen on
--zmq-rpc-bind-port arg (=18082, 28082 if 'testnet', 38082 if 'stagenet')
Port for ZMQ RPC server to listen on
--zmq-pub arg Address for ZMQ pub - tcp://ip:port or
ipc://path
--no-zmq Disable ZMQ RPC server
--data-dir arg (=/home/sidney/.bitmonero, /home/sidney/.bitmonero/testnet if 'testnet', /home/sidney/.bitmonero/stagenet if 'stagenet')
Specify data directory
--test-drop-download For net tests: in download, discard ALL
blocks instead checking/saving them
(very fast)
--test-drop-download-height arg (=0) Like test-drop-download but discards
only after around certain height
--testnet Run on testnet. The wallet must be
launched with --testnet flag.
--stagenet Run on stagenet. The wallet must be
launched with --stagenet flag.
--regtest Run in a regression testing mode.
--keep-fakechain Don't delete any existing database when
in fakechain mode.
--fixed-difficulty arg (=0) Fixed difficulty used for testing.
--enforce-dns-checkpointing checkpoints from DNS server will be
enforced
--prep-blocks-threads arg (=4) Max number of threads to use when
preparing block hashes in groups.
--fast-block-sync arg (=1) Sync up most of the way by using
embedded, known block hashes.
--show-time-stats arg (=0) Show time-stats when processing
blocks/txs and disk synchronization.
--block-sync-size arg (=0) How many blocks to sync at once during
chain synchronization (0 = adaptive).
--check-updates arg (=notify) Check for new versions of monero:
[disabled|notify|download|update]
--fluffy-blocks Relay blocks as fluffy blocks
(obsolete, now default)
--no-fluffy-blocks Relay blocks as normal blocks
--test-dbg-lock-sleep arg (=0) Sleep time in ms, defaults to 0 (off),
used to debug before/after locking
mutex. Values 100 to 1000 are good for
tests.
--offline Do not listen for peers, nor connect to
any
--disable-dns-checkpoints Do not retrieve checkpoints from DNS
--block-download-max-size arg (=0) Set maximum size of block download
queue in bytes (0 for default)
--sync-pruned-blocks Allow syncing from nodes with only
pruned blocks
--max-txpool-weight arg (=648000000) Set maximum txpool weight in bytes.
--block-notify arg Run a program for each new block, '%s'
will be replaced by the block hash
--prune-blockchain Prune blockchain
--reorg-notify arg Run a program for each reorg, '%s' will
be replaced by the split height, '%h'
will be replaced by the new blockchain
height, '%n' will be replaced by the
number of new blocks in the new chain,
and '%d' will be replaced by the number
of blocks discarded from the old chain
--block-rate-notify arg Run a program when the block rate
undergoes large fluctuations. This
might be a sign of large amounts of
hash rate going on and off the Monero
network, and thus be of potential
interest in predicting attacks. %t will
be replaced by the number of minutes
for the observation window, %b by the
number of blocks observed within that
window, and %e by the number of blocks
that was expected in that window. It is
suggested that this notification is
used to automatically increase the
number of confirmations required before
a payment is acted upon.
--keep-alt-blocks Keep alternative blocks on restart
--extra-messages-file arg Specify file for extra messages to
include into coinbase transactions
--start-mining arg Specify wallet address to mining for
--mining-threads arg Specify mining threads count
--bg-mining-enable enable background mining
--bg-mining-ignore-battery if true, assumes plugged in when unable
to query system power status
--bg-mining-min-idle-interval arg Specify min lookback interval in
seconds for determining idle state
--bg-mining-idle-threshold arg Specify minimum avg idle percentage
over lookback interval
--bg-mining-miner-target arg Specify maximum percentage cpu use by
miner(s)
--db-sync-mode arg (=fast:async:250000000bytes)
Specify sync option, using format
[safe|fast|fastest]:[sync|async]:[<nblo
cks_per_sync>[blocks]|<nbytes_per_sync>
[bytes]].
--db-salvage Try to salvage a blockchain database if
it seems corrupted
--p2p-bind-ip arg (=0.0.0.0) Interface for p2p network protocol
(IPv4)
--p2p-bind-ipv6-address arg (=::) Interface for p2p network protocol
(IPv6)
--p2p-bind-port arg (=18080, 28080 if 'testnet', 38080 if 'stagenet')
Port for p2p network protocol (IPv4)
--p2p-bind-port-ipv6 arg (=18080, 28080 if 'testnet', 38080 if 'stagenet')
Port for p2p network protocol (IPv6)
--p2p-use-ipv6 Enable IPv6 for p2p
--p2p-ignore-ipv4 Ignore unsuccessful IPv4 bind for p2p
--p2p-external-port arg (=0) External port for p2p network protocol
(if port forwarding used with NAT)
--allow-local-ip Allow local ip add to peer list, mostly
in debug purposes
--add-peer arg Manually add peer to local peerlist
--add-priority-node arg Specify list of peers to connect to and
attempt to keep the connection open
--add-exclusive-node arg Specify list of peers to connect to
only. If this option is given the
options add-priority-node and seed-node
are ignored
--seed-node arg Connect to a node to retrieve peer
addresses, and disconnect
--tx-proxy arg Send local txes through proxy:
<network-type>,<socks-ip:port>[,max_con
nections][,disable_noise] i.e.
"tor,127.0.0.1:9050,100,disable_noise"
--anonymous-inbound arg <hidden-service-address>,<[bind-ip:]por
t>[,max_connections] i.e.
"x.onion,127.0.0.1:18083,100"
--ban-list arg Specify ban list file, one IP address
per line
--hide-my-port Do not announce yourself as peerlist
candidate
--no-sync Don't synchronize the blockchain with
other peers
--enable-dns-blocklist Apply realtime blocklist from DNS
--no-igd Disable UPnP port mapping
--igd arg (=delayed) UPnP port mapping (disabled, enabled,
delayed)
--out-peers arg (=-1) set max number of out peers
--in-peers arg (=-1) set max number of in peers
--tos-flag arg (=-1) set TOS flag
--limit-rate-up arg (=2048) set limit-rate-up [kB/s]
--limit-rate-down arg (=8192) set limit-rate-down [kB/s]
--limit-rate arg (=-1) set limit-rate [kB/s]
--pad-transactions Pad relayed transactions to help defend
against traffic volume analysis
--max-connections-per-ip arg (=1) Maximum number of connections allowed
from the same IP address
--rpc-bind-port arg (=18081, 28081 if 'testnet', 38081 if 'stagenet')
Port for RPC server
--rpc-restricted-bind-port arg Port for restricted RPC server
--restricted-rpc Restrict RPC to view only commands and
do not return privacy sensitive data in
RPC calls
--bootstrap-daemon-address arg URL of a 'bootstrap' remote daemon that
the connected wallets can use while
this daemon is still not fully synced.
Use 'auto' to enable automatic public
nodes discovering and bootstrap daemon
switching
--bootstrap-daemon-login arg Specify username:password for the
bootstrap daemon login
--bootstrap-daemon-proxy arg <ip>:<port> socks proxy to use for
bootstrap daemon connections
--rpc-bind-ip arg (=127.0.0.1) Specify IP to bind RPC server
--rpc-bind-ipv6-address arg (=::1) Specify IPv6 address to bind RPC server
--rpc-restricted-bind-ip arg (=127.0.0.1)
Specify IP to bind restricted RPC
server
--rpc-restricted-bind-ipv6-address arg (=::1)
Specify IPv6 address to bind restricted
RPC server
--rpc-use-ipv6 Allow IPv6 for RPC
--rpc-ignore-ipv4 Ignore unsuccessful IPv4 bind for RPC
--rpc-login arg Specify username[:password] required
for RPC server
--confirm-external-bind Confirm rpc-bind-ip value is NOT a
loopback (local) IP
--rpc-access-control-origins arg Specify a comma separated list of
origins to allow cross origin resource
sharing
--rpc-ssl arg (=autodetect) Enable SSL on RPC connections:
enabled|disabled|autodetect
--rpc-ssl-private-key arg Path to a PEM format private key
--rpc-ssl-certificate arg Path to a PEM format certificate
--rpc-ssl-ca-certificates arg Path to file containing concatenated
PEM format certificate(s) to replace
system CA(s).
--rpc-ssl-allowed-fingerprints arg List of certificate fingerprints to
allow
--rpc-ssl-allow-chained Allow user (via --rpc-ssl-certificates)
chain certificates
--disable-rpc-ban Do not ban hosts on RPC errors
--rpc-ssl-allow-any-cert Allow any peer certificate
--rpc-payment-address arg Restrict RPC to clients sending
micropayment to this address
--rpc-payment-difficulty arg (=1000) Restrict RPC to clients sending
micropayment at this difficulty
--rpc-payment-credits arg (=100) Restrict RPC to clients sending
micropayment, yields that many credits
per payment
--rpc-payment-allow-free-loopback Allow free access from the loopback
address (ie, the local host)
*/

View file

@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SettingsRoutingModule } from './settings-routing.module';
@NgModule({
declarations: [],
imports: [
CommonModule,
SettingsRoutingModule
]
})
export class SettingsModule { }

View file

@ -20,6 +20,7 @@ export class SidebarComponent {
new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'),
new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'),
new NavLink('Bans', '/bans', 'bi bi-ban'),
new NavLink('Settings', '/settings', 'bi bi-gear')
];
this.isLoading = false;
this.errorMessage = '';

View file

@ -0,0 +1,136 @@
export class DaemonSettings {
public logFile: string = '';
public logLevel: number = 0;
public maxLogFileSize: number = 104850000;
public maxLogFiles: number = 50;
public maxConcurrency: number = 0;
public proxy: string = '';
public proxyAllowDnsLeaks: boolean = false;
public publicNode: boolean = false;
public zmqRpcBindIp: string = '';
public zmqRpcBindPort: number = 0;
public zmqPub: string = '';
public noZmq: boolean = false;
public testDropDownload: boolean = false;
public testDropDownloadHeight: number = 0;
public testDbgLockSleep: number = 0;
public testnet: boolean = false;
public mainnet: boolean = false;
public stagenet: boolean = false;
public regtest: boolean = false;
public keepFakeChain: boolean = false;
public fixedDifficulty: number = 0;
public enforceDnsCheckpoint: boolean = false;
public prepBlocksThreads: number = 0;
public fastBlockSync: boolean = false;
public showTimeStats: boolean = false;
public blockSyncSize: number = 0;
public checkUpdates: 'disabled' | 'notify' | 'download' | 'update' = 'notify';
public fluffyBlocks: boolean = true;
public noFluffyBlocks: boolean = false;
public offline: boolean = false;
public disableDnsCheckpoints: boolean = false;
public blockDownloadMaxSize: number = 0;
public syncPrunedBlocks: boolean = false;
public maxTxPoolWeight: number = 648000000;
public blockNotify: string = '';
public pruneBlockchain: boolean = false;
public reorgNotify: string = '';
public blockRateNotify: string = '';
public keepAltBlocks: boolean = false;
public extraMessagesFile: string = '';
public startMining: string = '';
public miningThreds: number = 0;
public bgMiningEnable: boolean = false;
public bgMiningIgnoreBattery: boolean = false;
public bgMiningMinIdleInterval: number = 0;
public bgMiningIdleThreshold: number = 0;
public bgMiningMinerTarget: number = 0;
public dbSyncMode: string = 'fast:async:250000000bytes';
public dbSalvage: boolean = false;
public p2pBindIp: string = '0.0.0.0';
public p2pBindIpv6Address: string = "::";
public p2pBindPort: number = 0;
public p2pBindPortIpv6: number = 0;
public p2pUseIpv6: boolean = false;
public p2pIgnoreIpv4: boolean = false;
public p2pExternalPort: number = 0;
public allowLocalIp: boolean = false;
public addPeer: string = '';
public addPriorityNode: string = '';
public addExclusiveNode: string = '';
public seedNode: string = '';
public txProxy: string = '';
public anonymousInbound: string = '';
public banList: string = '';
public hideMyPort: boolean = false;
public noSync: boolean = false;
public enableDnsBlocklist: boolean = false;
public noIgd: boolean = false;
public igd: 'disable' | 'enabled' | 'delayed' = 'delayed';
public outPeers: number = -1;
public inPeers: number = -1;
public tosFlag: number = -1;
public limitRateUp: number = 2048;
public limitRateDown: number = 8192;
public limitRate: number = -1;
public padTransactions: boolean = false;
public maxConnectionsPerIp: number = 1;
public rpcBindPort: number = 0;
public restrictedBindPort: number = 0;
public restrictedRpc: boolean = false;
public bootstrapDaemonAddress: string = '';
public bootstrapDaemonLogin: string = '';
public bootstrapDaemonProxy: string = '';
public confirmExternalBind: boolean = false;
public rpcBindIp: string = '127.0.0.1';
public rpcBindIpv6Address: string = '::1';
public rpcRestrictedBindIp: string = '';
public rpcUseIpv6: boolean = false;
public rpcIgnoreIpv4: boolean = false;
public rpcLogin: string = '';
public rpcAccessControlOrigins: string = '';
public rpcSsl: 'autodetect' | 'enabled' | 'disabled' = 'autodetect';
public rpcSslPrivateKey: string = '';
public rpcSslCertificate: string = '';
public rpcSslCACertificates: string = '';
public rpcAllowedFingerprints: string = '';
public rpcSslAllowChained: boolean = false;
public rpcSslAllowAnyCert: boolean = false;
public rpcPaymentAddress: string = '';
public rpcPaymentDifficuly: number = 1000;
public rpcPaymentCredits: number = 100;
public rpcPaymentAllowFreeLoopback: boolean = false;
public disableRpcBan: boolean = false;
public toCommandOptions(): string {
let options: string = '';
return options;
}
}

View file

@ -28,7 +28,7 @@ export class SyncInfo {
const rawPeers: any[] = syncInfo.peers;
const rawSpans: any[] | undefined = syncInfo.rawSpans;
rawPeers.forEach((peer: any) => peers.push(Peer.parse(peer)));
if (rawPeers) rawPeers.forEach((peer: any) => peers.push(Peer.parse(peer)));
if (rawSpans) rawSpans.forEach((span: any) => spans.push(Span.parse(span)));
return new SyncInfo(height, targetHeight, nextNeededPruningSeed, overview, peers, spans);

2
src/common/error.ts Normal file
View file

@ -0,0 +1,2 @@
export { RpcError } from "./error/RpcError";
export { CoreIsBusyError } from "./error/CoreIsBusyError";

View file

@ -0,0 +1,8 @@
import { RpcError } from "./RpcError";
export class CoreIsBusyError extends RpcError {
constructor() {
super(-9, "Core is busy");
}
}

View file

@ -0,0 +1,9 @@
export abstract class RpcError extends Error {
public readonly code: number;
constructor(code: number, message: string) {
super(message);
this.code = code;
}
}

View file

@ -25,4 +25,36 @@ export { GetTxPoolBacklogRequest } from "./request/GetTxPoolBacklogRequest";
export { PruneBlockchainRequest } from "./request/PruneBlockchainRequest";
export { CalculatePoWHashRequest } from "./request/CalculatePoWHashRequest";
export { FlushCacheRequest } from "./request/FlushCacheRequest";
export { GetMinerDataRequest } from "./request/GetMinerDataRequest";
export { GetMinerDataRequest } from "./request/GetMinerDataRequest";
export { GetCoinbaseTxSumRequest } from "./request/GetCoinbaseTxSumRequest";
export { EmptyRpcRequest } from "./request/EmptyRpcRequest";
/**
* Restricted requests
* flush_txpool
generateblocks
get_alternate_chains
get_bans
get_coinbase_tx_sum
get_peer_list
in_peers
mining_status
on_get_connections
on_set_bans
out_peers
relay_tx
save_bc
set_bans
set_limit
set_log_categories
set_log_hash_rate
set_log_level
start_mining
start_save_graph
stop_daemon
stop_mining
stop_save_graph
sync_info
update
*/

View file

@ -2,6 +2,8 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class BannedRequest extends JsonRPCRequest {
public override readonly method: string = 'banned';
public override readonly restricted: boolean = false;
public readonly address: string;
constructor(address: string) {

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class CalculatePoWHashRequest extends JsonRPCRequest {
public override readonly method: string = 'calc_pow';
public override readonly restricted: boolean = false;
public readonly majorVersion: number;
public readonly height: number;
public readonly blockBlob: string;

View file

@ -0,0 +1,6 @@
import { JsonRPCRequest } from "./JsonRPCRequest";
export class EmptyRpcRequest extends JsonRPCRequest {
public override readonly restricted: boolean = false;
public override readonly method: string = "";
}

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class FlushCacheRequest extends JsonRPCRequest {
public override readonly method: string = 'flush_cache';
public override readonly restricted: boolean = false;
public readonly badTxs: boolean;
public readonly badBlocks: boolean;

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class FlushTxPoolRequest extends JsonRPCRequest {
public override readonly method: string = 'flush_txpool';
public override readonly restricted: boolean = true;
public txIds: string[];
constructor(txIds: string[]) {

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GenerateBlocksRequest extends JsonRPCRequest {
public override method: string = "generateblocks";
public override readonly restricted: boolean = true;
public readonly amountOfBlocks: number;
public readonly walletAddress: string;
public readonly prevBlock: string;

View file

@ -2,5 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetAlternateChainsRequest extends JsonRPCRequest {
public override readonly method: string = 'get_alternate_chains';
public override readonly restricted: boolean = true;
}

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBansRequest extends JsonRPCRequest {
public override readonly method: string = 'get_bans';
public override readonly restricted: boolean = true;
}

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockCountRequest extends JsonRPCRequest {
public override readonly method: string = "get_block_count";
public override readonly restricted: boolean = false;
}

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockHashRequest extends JsonRPCRequest {
public override readonly method: string = "on_get_block_hash";
public override readonly restricted: boolean = false;
public readonly blockHeight: number;
constructor(blockHeight: number) {

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockHeaderByHashRequest extends JsonRPCRequest {
public override readonly method: string = 'get_block_header_by_hash';
public override readonly restricted: boolean = false;
public readonly hash: string;
public readonly fillPowHash: boolean;

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockHeaderByHeightRequest extends JsonRPCRequest {
public override readonly method: string = 'get_block_header_by_height';
public override readonly restricted: boolean = false;
public readonly height: number;
public readonly fillPowHash: boolean;

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockHeadersRangeRequest extends JsonRPCRequest {
public override readonly method: string = 'get_block_headers_range';
public override readonly restricted: boolean = false;
public readonly startHeight: number;
public readonly endHeight: number;
public readonly fillPowHash: boolean;

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockTemplateRequest extends JsonRPCRequest {
public override readonly method: string = "get_block_template";
public override readonly restricted: boolean = false;
public readonly walletAddress: string;
public readonly reserveSize: number;

View file

@ -0,0 +1,26 @@
import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetCoinbaseTxSumRequest extends JsonRPCRequest {
public override readonly method: string = "get_coinbase_tx_sum";
public override readonly restricted: boolean = true;
public readonly height: number;
public readonly count: number;
constructor(height: number, count: number) {
super();
this.height = height;
this.count = count;
}
public override toDictionary(): { [key: string]: any; } {
const dict = super.toDictionary();
dict['params'] = {
'height': this.height,
'count': this.count
};
return dict;
}
}

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetConnectionsRequest extends JsonRPCRequest {
public override readonly method: string = 'get_connections';
public override readonly restricted: boolean = false;
}

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetFeeEstimateRequest extends JsonRPCRequest {
public override readonly method: string = 'get_fee_estimate';
public override readonly restricted: boolean = false;
public readonly graceBlocks: number;
constructor(graceBlocks: number = 0) {

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetInfoRequest extends JsonRPCRequest {
public override readonly method: string = 'get_info';
public override readonly restricted: boolean = false;
}

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetLastBlockHeaderRequest extends JsonRPCRequest {
public override readonly method: string = 'get_last_block_header';
public override readonly restricted: boolean = false;
public readonly fillPowHash: boolean;
constructor(fillPowHash: boolean = false) {

View file

@ -2,6 +2,6 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetMinerDataRequest extends JsonRPCRequest {
public override readonly method: string = 'get_miner_data';
public override readonly restricted: boolean = false;
}

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetOutputHistogramRequest extends JsonRPCRequest {
public override readonly method: string = 'get_output_histogram';
public override readonly restricted: boolean = false;
public readonly amounts: number[];
public readonly minCount: number;
public readonly maxCount: number;

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetTxPoolBacklogRequest extends JsonRPCRequest {
public override readonly method: string = 'get_txpool_backlog';
public override readonly restricted: boolean = false;
}

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest"
export class GetVersionRequest extends JsonRPCRequest {
public override readonly method: string = 'get_version';
public override readonly restricted: boolean = false;
}

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class HardForkInfoRequest extends JsonRPCRequest {
public override readonly method: string = 'hard_fork_info';
public override readonly restricted: boolean = false;
}

View file

@ -2,6 +2,7 @@ export abstract class JsonRPCRequest {
public readonly version: string = "2.0";
public readonly id: string = "0";
public abstract readonly method: string;
public abstract readonly restricted: boolean;
public toDictionary(): { [key: string]: any } {
return {

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class PruneBlockchainRequest extends JsonRPCRequest {
public override readonly method: string = 'prune_blockchain';
public override readonly restricted: boolean = true;
public readonly check: boolean;
constructor(check: boolean = false) {

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class RelayTxRequest extends JsonRPCRequest {
public override readonly method: string = 'relay_tx';
public override readonly restricted: boolean = true;
public readonly txIds: string[];
constructor(txIds: string[]) {

View file

@ -3,6 +3,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class SetBansRequest extends JsonRPCRequest {
public override readonly method: string = 'set_bans';
public override readonly restricted: boolean = true;
public bans: Ban[];
constructor(bans: Ban[]) {

View file

@ -2,6 +2,7 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class SubmitBlockRequest extends JsonRPCRequest {
public override method: string = "submit_block";
public override readonly restricted: boolean = false;
public readonly blockBlobData: string[];
constructor(blockBlobData: string[]) {

View file

@ -2,4 +2,5 @@ import { JsonRPCRequest } from "./JsonRPCRequest";
export class SyncInfoRequest extends JsonRPCRequest {
public override readonly method: string = 'sync_info';
public override readonly restricted: boolean = true;
}

View file

@ -1,5 +1,5 @@
<!doctype html>
<html>
<html data-bs-theme="dark">
<head>
<meta charset="utf-8">
<title>Angular Electron</title>

View file

@ -51,3 +51,7 @@ import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
import 'jquery';
import * as $ from 'jquery';
import 'bootstrap-table';