mirror of
https://github.com/everoddandeven/monerod-gui.git
synced 2025-01-03 17:39:49 +00:00
Network methods implementation
Peers methods implementation Bans methods implementation
This commit is contained in:
parent
5d4280bcf8
commit
5705b8e91b
18 changed files with 804 additions and 84 deletions
|
@ -1,6 +1,6 @@
|
||||||
import { EventEmitter, Injectable } from '@angular/core';
|
import { EventEmitter, Injectable } from '@angular/core';
|
||||||
import { DaemonService } from './daemon.service';
|
import { DaemonService } from './daemon.service';
|
||||||
import { BlockCount, BlockHeader, Chain, CoreIsBusyError, DaemonInfo, MinerData, MiningStatus, NetStats, NetStatsHistory, PublicNode, SyncInfo } from '../../../../common';
|
import { BlockCount, BlockHeader, Chain, Connection, CoreIsBusyError, DaemonInfo, MinerData, MiningStatus, NetStats, NetStatsHistory, PeerInfo, PublicNode, SyncInfo, TxPool } from '../../../../common';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -12,6 +12,7 @@ export class DaemonDataService {
|
||||||
private _refreshing: boolean = false;
|
private _refreshing: boolean = false;
|
||||||
private _firstRefresh: boolean = true;
|
private _firstRefresh: boolean = true;
|
||||||
private _lastRefresh: number = Date.now();
|
private _lastRefresh: number = Date.now();
|
||||||
|
private _lastRefreshHeight: number = -1;
|
||||||
|
|
||||||
private _daemonRunning: boolean = false;
|
private _daemonRunning: boolean = false;
|
||||||
private _daemonRestarting: boolean = false;
|
private _daemonRestarting: boolean = false;
|
||||||
|
@ -48,6 +49,15 @@ export class DaemonDataService {
|
||||||
private _publicNodes: PublicNode[] = [];
|
private _publicNodes: PublicNode[] = [];
|
||||||
private _gettingPublicNodes: boolean = false;
|
private _gettingPublicNodes: boolean = false;
|
||||||
|
|
||||||
|
private _transactionPool?: TxPool;
|
||||||
|
private _gettingTransactionPool: boolean = false;
|
||||||
|
|
||||||
|
private _connections: Connection[] = [];
|
||||||
|
private _gettingConnections: boolean = false;
|
||||||
|
|
||||||
|
private _peerList: PeerInfo[] = [];
|
||||||
|
private _gettingPeerList: boolean = false;
|
||||||
|
|
||||||
public readonly syncStart: EventEmitter<void> = new EventEmitter<void>();
|
public readonly syncStart: EventEmitter<void> = new EventEmitter<void>();
|
||||||
public readonly syncEnd: EventEmitter<void> = new EventEmitter<void>();
|
public readonly syncEnd: EventEmitter<void> = new EventEmitter<void>();
|
||||||
public readonly syncError: EventEmitter<Error> = new EventEmitter<Error>();
|
public readonly syncError: EventEmitter<Error> = new EventEmitter<Error>();
|
||||||
|
@ -182,6 +192,30 @@ export class DaemonDataService {
|
||||||
return this._gettingPublicNodes;
|
return this._gettingPublicNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get transactionPool(): TxPool | undefined {
|
||||||
|
return this._transactionPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get gettingTransactionPool(): boolean {
|
||||||
|
return this._gettingTransactionPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get connections(): Connection[] {
|
||||||
|
return this._connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get gettingConnections(): boolean {
|
||||||
|
return this._gettingConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get peerList(): PeerInfo[] {
|
||||||
|
return this._peerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get gettingPeerList(): boolean {
|
||||||
|
return this._gettingPeerList;
|
||||||
|
}
|
||||||
|
|
||||||
public setRefreshTimeout(ms: number = 5000): void {
|
public setRefreshTimeout(ms: number = 5000): void {
|
||||||
this.refreshTimeoutMs = ms;
|
this.refreshTimeoutMs = ms;
|
||||||
}
|
}
|
||||||
|
@ -324,12 +358,25 @@ export class DaemonDataService {
|
||||||
|
|
||||||
await this.refreshMinerData();
|
await this.refreshMinerData();
|
||||||
|
|
||||||
|
this._gettingPeerList = true;
|
||||||
|
this._peerList = await this.daemonService.getPeerList();
|
||||||
|
this._gettingPeerList = false;
|
||||||
|
|
||||||
this._gettingPublicNodes = true;
|
this._gettingPublicNodes = true;
|
||||||
|
|
||||||
this._publicNodes = await this.daemonService.getPublicNodes(true, true);
|
this._publicNodes = await this.daemonService.getPublicNodes(true, true);
|
||||||
|
|
||||||
this._gettingPublicNodes = false;
|
this._gettingPublicNodes = false;
|
||||||
|
|
||||||
|
if (this._daemonInfo.synchronized && this._daemonInfo.txPoolSize > 0) {
|
||||||
|
this._gettingTransactionPool = true;
|
||||||
|
this._transactionPool = await this.daemonService.getTransactionPool();
|
||||||
|
this._gettingTransactionPool = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._gettingConnections = true;
|
||||||
|
this._connections = await this.daemonService.getConnections();
|
||||||
|
this._gettingConnections = false;
|
||||||
|
|
||||||
|
this._lastRefreshHeight = this._daemonInfo.heightWithoutBootstrap;
|
||||||
this._lastRefresh = Date.now();
|
this._lastRefresh = Date.now();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -341,6 +388,9 @@ export class DaemonDataService {
|
||||||
this._gettingAltChains = false;
|
this._gettingAltChains = false;
|
||||||
this._gettingNetStats = false;
|
this._gettingNetStats = false;
|
||||||
this._gettingPublicNodes = false;
|
this._gettingPublicNodes = false;
|
||||||
|
this._gettingTransactionPool = false;
|
||||||
|
this._gettingConnections = false;
|
||||||
|
this._gettingPeerList = false;
|
||||||
|
|
||||||
this.syncError.emit(<Error>error);
|
this.syncError.emit(<Error>error);
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,9 @@ import {
|
||||||
SetBootstrapDaemonRequest,
|
SetBootstrapDaemonRequest,
|
||||||
SetLogLevelRequest,
|
SetLogLevelRequest,
|
||||||
SetLogHashRateRequest,
|
SetLogHashRateRequest,
|
||||||
SetLogCategoriesRequest
|
SetLogCategoriesRequest,
|
||||||
|
GetTransactionPoolRequest,
|
||||||
|
GetPeerListRequest
|
||||||
} from '../../../../common/request';
|
} from '../../../../common/request';
|
||||||
import { BlockTemplate } from '../../../../common/BlockTemplate';
|
import { BlockTemplate } from '../../../../common/BlockTemplate';
|
||||||
import { GeneratedBlocks } from '../../../../common/GeneratedBlocks';
|
import { GeneratedBlocks } from '../../../../common/GeneratedBlocks';
|
||||||
|
@ -76,6 +78,7 @@ import { TxInfo } from '../../../../common/TxInfo';
|
||||||
import { DaemonSettings } from '../../../../common/DaemonSettings';
|
import { DaemonSettings } from '../../../../common/DaemonSettings';
|
||||||
import { MethodNotFoundError } from '../../../../common/error/MethodNotFoundError';
|
import { MethodNotFoundError } from '../../../../common/error/MethodNotFoundError';
|
||||||
import { openDB, IDBPDatabase } from "idb"
|
import { openDB, IDBPDatabase } from "idb"
|
||||||
|
import { PeerInfo, TxPool } from '../../../../common';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -411,7 +414,12 @@ export class DaemonService {
|
||||||
|
|
||||||
public async getConnections(): Promise<Connection[]> {
|
public async getConnections(): Promise<Connection[]> {
|
||||||
const response = await this.callRpc(new GetConnectionsRequest());
|
const response = await this.callRpc(new GetConnectionsRequest());
|
||||||
const connections: any[] = response.connections;
|
|
||||||
|
if (!response.result || !response.result.connections) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const connections: any[] = response.result.connections;
|
||||||
const result: Connection[] = [];
|
const result: Connection[] = [];
|
||||||
|
|
||||||
connections.forEach((connection: any) => result.push(Connection.parse(connection)))
|
connections.forEach((connection: any) => result.push(Connection.parse(connection)))
|
||||||
|
@ -797,6 +805,23 @@ export class DaemonService {
|
||||||
return NetStats.parse(response);
|
return NetStats.parse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getPeerList(): Promise<PeerInfo[]> {
|
||||||
|
const response = await this.callRpc(new GetPeerListRequest());
|
||||||
|
|
||||||
|
if (response.status != 'OK') {
|
||||||
|
throw new Error(response.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const peerList: PeerInfo[] = [];
|
||||||
|
const whiteList: any[] | undefined = response.white_list
|
||||||
|
const grayList: any[] | undefined = response.gray_list;
|
||||||
|
|
||||||
|
if (whiteList) whiteList.forEach((white: any) => peerList.push(PeerInfo.parse(white, 'white')));
|
||||||
|
if (grayList) grayList.forEach((gray: any) => peerList.push(PeerInfo.parse(gray, 'gray')));
|
||||||
|
|
||||||
|
return peerList;
|
||||||
|
}
|
||||||
|
|
||||||
public async getPublicNodes(whites: boolean = true, grays: boolean = false, includeBlocked: boolean = false): Promise<PublicNode[]> {
|
public async getPublicNodes(whites: boolean = true, grays: boolean = false, includeBlocked: boolean = false): Promise<PublicNode[]> {
|
||||||
const response = await this.callRpc(new GetPublicNodesRequest(whites, grays, includeBlocked));
|
const response = await this.callRpc(new GetPublicNodesRequest(whites, grays, includeBlocked));
|
||||||
|
|
||||||
|
@ -810,6 +835,16 @@ export class DaemonService {
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getTransactionPool(): Promise<TxPool> {
|
||||||
|
const response = await this.callRpc(new GetTransactionPoolRequest());
|
||||||
|
|
||||||
|
if (response.status != 'OK') {
|
||||||
|
throw new Error(response.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TxPool.parse(response);
|
||||||
|
}
|
||||||
|
|
||||||
public async getTransactionPoolHashes(): Promise<string[]> {
|
public async getTransactionPoolHashes(): Promise<string[]> {
|
||||||
const response = await this.callRpc(new GetTransactionPoolHashesRequest());
|
const response = await this.callRpc(new GetTransactionPoolHashesRequest());
|
||||||
|
|
||||||
|
|
|
@ -11,21 +11,91 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [hidden]="!daemonRunning || daemonChangingStatus">
|
<div [hidden]="!daemonRunning || daemonStopping" class="tab-content" id="pills-tabContent">
|
||||||
<table
|
<div class="tab-pane fade show active" id="pills-overview" role="tabpanel" aria-labelledby="pills-overview-tab" tabindex="0">
|
||||||
id="bansTable"
|
<h4 class="mb-3">List of banned IPs</h4>
|
||||||
data-toggle="bansTable"
|
<br>
|
||||||
data-toolbar="#toolbar"
|
<table
|
||||||
data-height="460"
|
id="bansTable"
|
||||||
>
|
data-toggle="bansTable"
|
||||||
<thead>
|
data-toolbar="#toolbar"
|
||||||
<tr>
|
data-height="460"
|
||||||
<th data-field="host">Host</th>
|
>
|
||||||
<th data-field="ip">Ip</th>
|
<thead>
|
||||||
<th data-field="seconds">Seconds</th>
|
<tr>
|
||||||
</tr>
|
<th data-field="host">Host</th>
|
||||||
</thead>
|
<th data-field="ip">Ip</th>
|
||||||
</table>
|
<th data-field="seconds">Seconds</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<button *ngIf="!refreshingBansTable" class="w-100 btn btn-primary btn-lg" type="button" (click)="refreshBansTable()">Refresh Bans</button>
|
||||||
|
<button *ngIf="refreshingBansTable" class="w-100 btn btn-primary btn-lg" type="button" disabled>Refreshing Bans ...</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pills-set-bans" role="tabpanel" aria-labelledby="pills-set-bans-tab" tabindex="0">
|
||||||
|
<h4 class="mb-3">Ban another node by IP</h4>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row g-5 p-2">
|
||||||
|
<div class="col-md-7 col-lg-12">
|
||||||
|
<div *ngIf="setBansSuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
||||||
|
<h4><i class="bi bi-send-check m-2"></i></h4>
|
||||||
|
<div>
|
||||||
|
Successfully set bans
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="setBansError" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
|
||||||
|
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>
|
||||||
|
<div>
|
||||||
|
{{setBansError}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="needs-validation" novalidate="">
|
||||||
|
<div class="row g-3">
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="bans" class="form-label">Bans</label>
|
||||||
|
<textarea [(ngModel)]="setBansBansJsonString" [ngModelOptions]="{standalone: true}" type="text" class="form-control" id="bans" placeholder="[
|
||||||
|
{
|
||||||
|
'host': <string>,
|
||||||
|
'ip': <number>,
|
||||||
|
'ban': <boolean>,
|
||||||
|
'seconds': <number>
|
||||||
|
},
|
||||||
|
... ,
|
||||||
|
{
|
||||||
|
'host': <string>,
|
||||||
|
'ip': <number>,
|
||||||
|
'ban': <boolean>,
|
||||||
|
'seconds': <number>
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
rows="15" cols="15" ></textarea>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
Invalid bans provided.
|
||||||
|
</div>
|
||||||
|
<small class="text-body-secondary">List of nodes to ban</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<button *ngIf="!settingBans" class="w-100 btn btn-primary btn-lg" type="button" (click)="setBans()">Set Bans</button>
|
||||||
|
<button *ngIf="settingBans" class="w-100 btn btn-primary btn-lg" type="button" disabled>Setting Bans ...</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { AfterViewInit, Component, NgZone } from '@angular/core';
|
import { AfterViewInit, Component, NgZone } from '@angular/core';
|
||||||
import { NavigationEnd, Router } from '@angular/router';
|
|
||||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||||
import { DaemonService } from '../../core/services/daemon/daemon.service';
|
import { DaemonService } from '../../core/services/daemon/daemon.service';
|
||||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||||
|
import { DaemonDataService } from '../../core/services';
|
||||||
|
import { Ban } from '../../../common';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -15,61 +16,85 @@ export class BansComponent implements AfterViewInit {
|
||||||
new NavbarLink('pills-overview-tab', '#pills-overview', 'pills-overview', true, 'Overview', true),
|
new NavbarLink('pills-overview-tab', '#pills-overview', 'pills-overview', true, 'Overview', true),
|
||||||
new NavbarLink('pills-set-bans-tab', '#pills-set-bans', 'pills-set-bans', false, 'Set Bans', true)
|
new NavbarLink('pills-set-bans-tab', '#pills-set-bans', 'pills-set-bans', false, 'Set Bans', true)
|
||||||
];
|
];
|
||||||
public daemonRunning: boolean = false;
|
|
||||||
|
public refreshingBansTable: boolean = false;
|
||||||
|
|
||||||
|
public get daemonRunning(): boolean {
|
||||||
|
return this.daemonData.running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get daemonStopping(): boolean {
|
||||||
|
return this.daemonData.stopping;
|
||||||
|
}
|
||||||
|
|
||||||
public get daemonChangingStatus(): boolean {
|
public get daemonChangingStatus(): boolean {
|
||||||
return this.daemonService.stopping || this.daemonService.starting;
|
return this.daemonService.stopping || this.daemonService.starting;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
|
public settingBans: boolean = false;
|
||||||
this.router.events.subscribe((event) => {
|
public setBansBansJsonString: string = '';
|
||||||
if (event instanceof NavigationEnd) {
|
public setBansSuccess: boolean = false;
|
||||||
if (event.url != '/bans') return;
|
public setBansError: string = '';
|
||||||
this.onNavigationEnd();
|
|
||||||
|
public get validBans(): boolean {
|
||||||
|
try {
|
||||||
|
const bans: any[] = JSON.parse(this.setBansBansJsonString);
|
||||||
|
|
||||||
|
if (!Array.isArray(bans)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
this.daemonService.isRunning().then((running) => {
|
bans.forEach((ban) => Ban.parse(ban));
|
||||||
this.daemonRunning = running
|
|
||||||
});
|
|
||||||
|
|
||||||
this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => {
|
|
||||||
this.daemonRunning = running;
|
|
||||||
|
|
||||||
if (running) this.load();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
private get bans(): Ban[] {
|
||||||
this.navbarService.removeLinks();
|
if (!this.validBans) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const bans: Ban[] = [];
|
||||||
|
const rawBans: any[] = [];
|
||||||
|
|
||||||
console.log('BansComponent AFTER VIEW INIT');
|
rawBans.forEach((rawBan) => bans.push(Ban.parse(rawBan)));
|
||||||
|
|
||||||
|
return bans;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private daemonData: DaemonDataService, private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngAfterViewInit(): void {
|
||||||
|
this.navbarService.setLinks(this.navbarLinks);
|
||||||
|
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
//const $ = require('jquery');
|
|
||||||
//const bootstrapTable = require('bootstrap-table');
|
|
||||||
|
|
||||||
const $table = $('#bansTable');
|
const $table = $('#bansTable');
|
||||||
$table.bootstrapTable({
|
$table.bootstrapTable({});
|
||||||
|
|
||||||
});
|
|
||||||
$table.bootstrapTable('refreshOptions', {
|
$table.bootstrapTable('refreshOptions', {
|
||||||
classes: 'table table-bordered table-hover table-dark table-striped'
|
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||||
});
|
});
|
||||||
$table.bootstrapTable('showLoading');
|
$table.bootstrapTable('showLoading');
|
||||||
this.load();
|
this.refreshBansTable();
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onNavigationEnd(): void {
|
public async refreshBansTable(): Promise<void> {
|
||||||
this.navbarService.removeLinks();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async load(): Promise<void> {
|
|
||||||
const $table = $('#bansTable');
|
const $table = $('#bansTable');
|
||||||
|
let _bans: Ban[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
_bans = await this.daemonService.getBans();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
_bans = [];
|
||||||
|
}
|
||||||
|
|
||||||
const _bans = await this.daemonService.getBans();
|
|
||||||
const bans: any[] = [];
|
const bans: any[] = [];
|
||||||
|
|
||||||
_bans.forEach((ban) => bans.push({
|
_bans.forEach((ban) => bans.push({
|
||||||
|
@ -82,8 +107,21 @@ export class BansComponent implements AfterViewInit {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setBans() {
|
public async setBans(): Promise<void> {
|
||||||
|
this.settingBans = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.daemonService.setBans(...this.bans);
|
||||||
|
this.setBansError = '';
|
||||||
|
this.setBansSuccess = true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
this.setBansSuccess = false;
|
||||||
|
this.setBansError = `${error}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settingBans = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
|
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
|
||||||
|
|
||||||
<div class="tab-pane fade show active" id="pills-net-stats" role="tabpanel" aria-labelledby="pills-net-stats-tab" tabindex="0">
|
<div class="tab-pane fade show active" id="pills-net-stats" role="tabpanel" aria-labelledby="pills-net-stats-tab" tabindex="0">
|
||||||
<h2>Bytes In</h2>
|
<h2>Bytes In</h2>
|
||||||
<canvas class="my-4 w-100" id="netStatsBytesInChart" width="900" height="380"></canvas>
|
<canvas class="my-4 w-100" id="netStatsBytesInChart" width="900" height="380"></canvas>
|
||||||
|
@ -19,6 +20,91 @@
|
||||||
<h2>Bytes Out</h2>
|
<h2>Bytes Out</h2>
|
||||||
<canvas class="my-4 w-100" id="netStatsBytesOutChart" width="900" height="380"></canvas>
|
<canvas class="my-4 w-100" id="netStatsBytesOutChart" width="900" height="380"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pills-connections" role="tabpanel" aria-labelledby="pills-connections-tab" tabindex="0">
|
||||||
|
<h4 class="mb-3">Information about incoming and outgoing connections to your node</h4>
|
||||||
|
<br>
|
||||||
|
<div class="m-3">
|
||||||
|
<table
|
||||||
|
id="connectionsTable"
|
||||||
|
data-toggle="connectionsTable"
|
||||||
|
data-toolbar="#toolbar"
|
||||||
|
data-height="460"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-field="connectionId">ID</th>
|
||||||
|
<th data-field="peerId">Peer ID</th>
|
||||||
|
<th data-field="address">Address</th>
|
||||||
|
<th data-field="host">Host</th>
|
||||||
|
<th data-field="ip">IP</th>
|
||||||
|
<th data-field="port">Port</th>
|
||||||
|
<th data-field="state">State</th>
|
||||||
|
<th data-field="avgDownload">Avg Download</th>
|
||||||
|
<th data-field="avgUpload">Avg Upload</th>
|
||||||
|
<th data-field="currentDownload">Current Download</th>
|
||||||
|
<th data-field="currentUpload">Current Upload</th>
|
||||||
|
<th data-field="height">Height</th>
|
||||||
|
<th data-field="incoming">Incoming</th>
|
||||||
|
<th data-field="liveTime">Live Time</th>
|
||||||
|
<th data-field="recvCount">Recv Count</th>
|
||||||
|
<th data-field="recvIdleTime">Recv Idle Time</th>
|
||||||
|
<th data-field="sendCount">Send Count</th>
|
||||||
|
<th data-field="sendIdleTime">Send Idle Time</th>
|
||||||
|
<th data-field="supportFlags">Support Flags</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pills-limit" role="tabpanel" aria-labelledby="pills-limit-tab" tabindex="0">
|
||||||
|
<h4 class="mb-4">Set daemon bandwidth limits</h4>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div *ngIf="setLimitError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
|
||||||
|
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>
|
||||||
|
<div>
|
||||||
|
{{setLimitError}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="setLimitSuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
||||||
|
<h4><i class="bi bi-check-circle m-2"></i></h4>
|
||||||
|
<div>
|
||||||
|
Limit set to <strong>UP:</strong>{{setLimitResult?.limitUp}}, <strong>DOWN:</strong>{{setLimitResult?.limitDown}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-5 p-2">
|
||||||
|
<div class="cold-md-7 col-lg-12">
|
||||||
|
<div class="row gy-3">
|
||||||
|
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<label for="limit-down" class="form-label">Limit Down</label>
|
||||||
|
<input type="number" min="-1" class="form-control" id="limit-down" placeholder="" [(ngModel)]="limitDown" [ngModelOptions]="{standalone: true}">
|
||||||
|
<small class="text-body-secondary">Download limit in kBytes per second (-1 reset to default, 0 don't change the current limit)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<label for="limit-up" class="form-label">Limit Up</label>
|
||||||
|
<input type="number" min="-1" class="form-control" id="limit-up" placeholder="" [(ngModel)]="limitUp" [ngModelOptions]="{standalone: true}">
|
||||||
|
<small class="text-body-secondary">Upload limit in kBytes per second (-1 reset to default, 0 don't change the current limit)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<button *ngIf="!limiting" class="w-100 btn btn-primary btn-lg" type="button" (click)="setLimit()">Set Limit</button>
|
||||||
|
<button *ngIf="limiting" class="w-100 btn btn-primary btn-lg" type="button" disabled>Setting Limit ...</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-daemon-not-running></app-daemon-not-running>
|
<app-daemon-not-running></app-daemon-not-running>
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { AfterViewInit, Component } from '@angular/core';
|
import { AfterViewInit, Component, OnDestroy } from '@angular/core';
|
||||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||||
import { DaemonDataService, DaemonService } from '../../core/services';
|
import { DaemonDataService, DaemonService } from '../../core/services';
|
||||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||||
import { Chart, ChartData } from 'chart.js/auto'
|
import { Chart, ChartData } from 'chart.js/auto'
|
||||||
import { NetStatsHistoryEntry } from '../../../common';
|
import { NetStatsHistoryEntry } from '../../../common';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-network',
|
selector: 'app-network',
|
||||||
templateUrl: './network.component.html',
|
templateUrl: './network.component.html',
|
||||||
styleUrl: './network.component.scss'
|
styleUrl: './network.component.scss'
|
||||||
})
|
})
|
||||||
export class NetworkComponent implements AfterViewInit {
|
export class NetworkComponent implements AfterViewInit, OnDestroy {
|
||||||
public readonly navbarLinks: NavbarLink[];
|
public readonly navbarLinks: NavbarLink[];
|
||||||
|
|
||||||
private netStatsBytesInChart?: Chart;
|
private netStatsBytesInChart?: Chart;
|
||||||
|
@ -24,15 +25,29 @@ export class NetworkComponent implements AfterViewInit {
|
||||||
return this.daemonData.stopping;
|
return this.daemonData.stopping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public limiting: boolean = false;
|
||||||
|
public limitUp: number = 0;
|
||||||
|
public limitDown: number = 0;
|
||||||
|
public setLimitResult?: { limitUp: number, limitDown: number };
|
||||||
|
public setLimitSuccess: boolean = false;
|
||||||
|
public setLimitError: string = '';
|
||||||
|
|
||||||
|
private subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
constructor(private navbarService: NavbarService, private daemonService: DaemonService, private daemonData: DaemonDataService) {
|
constructor(private navbarService: NavbarService, private daemonService: DaemonService, private daemonData: DaemonDataService) {
|
||||||
this.navbarLinks = [
|
this.navbarLinks = [
|
||||||
new NavbarLink('pills-net-stats-tab', '#pills-net-stats', 'pills-net-stats', false, 'Statistics'),
|
new NavbarLink('pills-net-stats-tab', '#pills-net-stats', 'pills-net-stats', false, 'Statistics'),
|
||||||
new NavbarLink('pills-limits-tab', '#pills-limits', 'pills-limits', false, 'Limits')
|
new NavbarLink('pills-connections-tab', '#pills-connections', 'connections', false, 'Connetions'),
|
||||||
|
new NavbarLink('pills-limits-tab', '#pills-limit', 'pills-limit', false, 'Limit')
|
||||||
];
|
];
|
||||||
|
|
||||||
this.daemonData.netStatsRefreshEnd.subscribe(() => {
|
this.subscriptions.push(this.daemonData.netStatsRefreshEnd.subscribe(() => {
|
||||||
this.refreshNetStatsHistory();
|
this.refreshNetStatsHistory();
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
this.subscriptions.push(this.daemonData.syncEnd.subscribe(() => {
|
||||||
|
this.loadConnectionsTable();
|
||||||
|
}));
|
||||||
|
|
||||||
this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => {
|
this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => {
|
||||||
if (!running) {
|
if (!running) {
|
||||||
|
@ -47,6 +62,29 @@ export class NetworkComponent implements AfterViewInit {
|
||||||
public ngAfterViewInit(): void {
|
public ngAfterViewInit(): void {
|
||||||
this.navbarService.setLinks(this.navbarLinks);
|
this.navbarService.setLinks(this.navbarLinks);
|
||||||
this.initNetStatsHistoryChart();
|
this.initNetStatsHistoryChart();
|
||||||
|
this.initConnectionsTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.subscriptions.forEach((sub) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriptions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private initConnectionsTable(): void {
|
||||||
|
const $table = $('#connectionsTable');
|
||||||
|
$table.bootstrapTable({});
|
||||||
|
$table.bootstrapTable('refreshOptions', {
|
||||||
|
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadConnectionsTable(): void {
|
||||||
|
const $table = $('#connectionsTable');
|
||||||
|
|
||||||
|
$table.bootstrapTable('load', this.daemonData.connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildChartBytesInData(): ChartData {
|
private buildChartBytesInData(): ChartData {
|
||||||
|
@ -159,4 +197,22 @@ export class NetworkComponent implements AfterViewInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async setLimit(): Promise<void> {
|
||||||
|
this.limiting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.setLimitResult = await this.daemonService.setLimit(this.limitUp, this.limitDown);
|
||||||
|
this.setLimitSuccess = true;
|
||||||
|
this.setLimitError = '';
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
this.setLimitResult = undefined;
|
||||||
|
this.setLimitSuccess = false;
|
||||||
|
this.setLimitError = `${error}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.limiting = false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,38 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
|
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
|
||||||
<div class="tab-pane fade show active" id="pills-public-nodes" role="tabpanel" aria-labelledby="pills-public-nodes-tab" tabindex="0">
|
<div class="tab-pane fade show active" id="pills-peer-list" role="tabpanel" aria-labelledby="pills-peer-list-tab" tabindex="0">
|
||||||
|
<h4 class="mb-3">List of known peers</h4>
|
||||||
|
<div class="m-3">
|
||||||
|
<table
|
||||||
|
id="peerListTable"
|
||||||
|
data-toggle="peerListTable"
|
||||||
|
data-toolbar="#toolbar"
|
||||||
|
data-pagination="true"
|
||||||
|
data-height="460"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-field="host">Host</th>
|
||||||
|
<th data-field="id">ID</th>
|
||||||
|
<th data-field="ip">IP</th>
|
||||||
|
<th data-field="lastSeen">Last Seen</th>
|
||||||
|
<th data-field="port">Port</th>
|
||||||
|
<th data-field="type">Type</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<button *ngIf="!refreshingPeerList" class="w-100 btn btn-primary btn-lg" type="button" (click)="refreshPeerListTable()">Refresh Peer List</button>
|
||||||
|
<button *ngIf="refreshingPeerList" class="w-100 btn btn-primary btn-lg" type="button" disabled>Refreshing Peer List ...</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pills-public-nodes" role="tabpanel" aria-labelledby="pills-public-nodes-tab" tabindex="0">
|
||||||
<h4 class="mb-3">Public peer information</h4>
|
<h4 class="mb-3">Public peer information</h4>
|
||||||
<div class="m-3">
|
<div class="m-3">
|
||||||
<table
|
<table
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { DaemonDataService, DaemonService } from '../../core/services';
|
||||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-peers',
|
selector: 'app-peers',
|
||||||
|
@ -25,6 +26,8 @@ export class PeersComponent implements AfterViewInit, OnDestroy {
|
||||||
public limitOutPeersError: string = '';
|
public limitOutPeersError: string = '';
|
||||||
public limitOutPeersResult: number = 0;
|
public limitOutPeersResult: number = 0;
|
||||||
|
|
||||||
|
public refreshingPeerList: boolean = false;
|
||||||
|
|
||||||
public get daemonRunning(): boolean {
|
public get daemonRunning(): boolean {
|
||||||
return this.daemonData.running;
|
return this.daemonData.running;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +40,7 @@ export class PeersComponent implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
constructor(private daemonService: DaemonService, private daemonData: DaemonDataService, private navbarService: NavbarService, private ngZone: NgZone) {
|
constructor(private daemonService: DaemonService, private daemonData: DaemonDataService, private navbarService: NavbarService, private ngZone: NgZone) {
|
||||||
this.navbarLinks = [
|
this.navbarLinks = [
|
||||||
|
new NavbarLink('pills-peer-list-tab', '#pills-peer-list', 'pills-peer-list', false, 'Peer List'),
|
||||||
new NavbarLink('pills-public-nodes-tab', '#pills-public-nodes', 'pills-public-nodes', false, 'Public Nodes'),
|
new NavbarLink('pills-public-nodes-tab', '#pills-public-nodes', 'pills-public-nodes', false, 'Public Nodes'),
|
||||||
new NavbarLink('pills-in-peers-tab', '#pills-in-peers', 'pills-in-peers', false, 'In Peers'),
|
new NavbarLink('pills-in-peers-tab', '#pills-in-peers', 'pills-in-peers', false, 'In Peers'),
|
||||||
new NavbarLink('pills-out-peers-tab', '#pills-out-peers', 'pills-out-peers', false, 'Out Peers')
|
new NavbarLink('pills-out-peers-tab', '#pills-out-peers', 'pills-out-peers', false, 'Out Peers')
|
||||||
|
@ -48,21 +52,56 @@ export class PeersComponent implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
const $publicNodesTable = $('#publicNodesTable');
|
const $publicNodesTable = $('#publicNodesTable');
|
||||||
|
const $peerListTable = $('#peerListTable');
|
||||||
|
|
||||||
$publicNodesTable.bootstrapTable({});
|
$publicNodesTable.bootstrapTable({});
|
||||||
$publicNodesTable.bootstrapTable('refreshOptions', {
|
$publicNodesTable.bootstrapTable('refreshOptions', {
|
||||||
classes: 'table table-bordered table-hover table-dark table-striped'
|
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$peerListTable.bootstrapTable({});
|
||||||
|
$peerListTable.bootstrapTable('refreshOptions', {
|
||||||
|
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||||
|
});
|
||||||
|
|
||||||
$publicNodesTable.bootstrapTable('load', this.daemonData.publicNodes);
|
$publicNodesTable.bootstrapTable('load', this.daemonData.publicNodes);
|
||||||
|
$peerListTable.bootstrapTable('load', this.daemonData.peerList);
|
||||||
|
|
||||||
const sub = this.daemonData.syncEnd.subscribe(() => {
|
const sub = this.daemonData.syncEnd.subscribe(() => {
|
||||||
$publicNodesTable.bootstrapTable('load', this.daemonData.publicNodes);
|
$publicNodesTable.bootstrapTable('load', this.daemonData.publicNodes);
|
||||||
|
//$peerListTable.bootstrapTable('load', this.daemonData.peerList);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.subscriptions.push(sub);
|
this.subscriptions.push(sub);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async refreshPeerListTable(): Promise<void> {
|
||||||
|
this.refreshingPeerList = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.ngZone.run(() => {
|
||||||
|
try {
|
||||||
|
const $peerListTable = $('#peerListTable');
|
||||||
|
$peerListTable.bootstrapTable('load', this.daemonData.peerList);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refreshingPeerList = false;
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
this.subscriptions.forEach((sub) => {
|
this.subscriptions.forEach((sub) => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
|
|
|
@ -11,9 +11,63 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="daemonRunning" class="tab-content" id="pills-tabContent">
|
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
|
||||||
|
|
||||||
|
<div class="tab-pane fade show active" id="pills-tx-pool" role="tabpanel" aria-labelledby="pills-tx-pool-tab" tabindex="0">
|
||||||
|
<h4 class="mb-3">Information about valid transactions seen by the node but not yet mined into a block, as well as spent key image information for the txpool in the node's memory</h4>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h6 class="mb-3">Transactions</h6>
|
||||||
|
<div class="m-3">
|
||||||
|
<table
|
||||||
|
id="transactionsTable"
|
||||||
|
data-toggle="transactionsTable"
|
||||||
|
data-toolbar="#toolbar"
|
||||||
|
data-height="460"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-field="idHash">ID Hash</th>
|
||||||
|
<th data-field="relayed">Relayed</th>
|
||||||
|
<th data-field="blobSize">Blob Size</th>
|
||||||
|
<th data-field="doNotRelay">Do Not Relay</th>
|
||||||
|
<th data-field="doubleSpendSeen">Double Spend Seen</th>
|
||||||
|
<th data-field="fee">Fee</th>
|
||||||
|
<th data-field="keptByBlock">Kept By Block</th>
|
||||||
|
<th data-field="lastFailedHeight">Last Failed Height</th>
|
||||||
|
<th data-field="lastFailedIdHash">Last Failed Id Hash</th>
|
||||||
|
<th data-field="lastRelayedTime">Last Relayed Time</th>
|
||||||
|
<th data-field="maxUsedBlockHeight">Max Used Block Height</th>
|
||||||
|
<th data-field="maxUsedBlockIdHash">Max Used Block Id Hash</th>
|
||||||
|
<th data-field="receiveTime">Receive Time</th>
|
||||||
|
<th data-field="txBlob">Tx Blob</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<h6 class="mb-3">Spent Key Images</h6>
|
||||||
|
<div class="m-3">
|
||||||
|
<table
|
||||||
|
id="spentKeyImagesTable"
|
||||||
|
data-toggle="spentKeyImagesTable"
|
||||||
|
data-toolbar="#toolbar"
|
||||||
|
data-height="460"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-field="idHash">ID Hash</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pills-relay-tx" role="tabpanel" aria-labelledby="pills-relay-tx-tab" tabindex="0">
|
||||||
|
|
||||||
<div class="tab-pane fade show active" id="pills-relay-tx" role="tabpanel" aria-labelledby="pills-relay-tx-tab" tabindex="0">
|
|
||||||
<div class="row g-5 p-2">
|
<div class="row g-5 p-2">
|
||||||
<div class="col-md-7 col-lg-12">
|
<div class="col-md-7 col-lg-12">
|
||||||
<div *ngIf="relaySuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
<div *ngIf="relaySuccess" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
|
||||||
|
@ -56,6 +110,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" [disabled]="!validTxIds()" (click)="onRelay()">Relay Tx</button>
|
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" [disabled]="!validTxIds()" (click)="onRelay()">Relay Tx</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="pills-send-raw-tx" role="tabpanel" aria-labelledby="pills-send-raw-tx-tab" tabindex="0">
|
<div class="tab-pane fade" id="pills-send-raw-tx" role="tabpanel" aria-labelledby="pills-send-raw-tx-tab" tabindex="0">
|
||||||
|
@ -95,12 +150,12 @@
|
||||||
<br>
|
<br>
|
||||||
<small class="text-body-secondary">Stop relaying transaction to other nodes</small>
|
<small class="text-body-secondary">Stop relaying transaction to other nodes</small>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-4">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr class="my-4">
|
||||||
<button class="w-100 btn btn-primary btn-lg" type="button" (click)="sendRawTx()">Send Raw Tx</button>
|
<button class="w-100 btn btn-primary btn-lg" type="button" (click)="sendRawTx()">Send Raw Tx</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -152,7 +207,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="pills-flush-tx-pool" role="tabpanel" aria-labelledby="pills-flush-tx-pool-tab" tabindex="0">
|
<div class="tab-pane fade" id="pills-flush-tx-pool" role="tabpanel" aria-labelledby="pills-flush-tx-pool-tab" tabindex="0">
|
||||||
<div class="row g-5 m-2">
|
<div class="row g-5 p-2">
|
||||||
<div class="col-md-7 col-lg-10">
|
<div class="col-md-7 col-lg-10">
|
||||||
<h4 class="mb-3">Flush a list of transaction IDs</h4>
|
<h4 class="mb-3">Flush a list of transaction IDs</h4>
|
||||||
<form class="needs-validation" novalidate="">
|
<form class="needs-validation" novalidate="">
|
||||||
|
@ -180,7 +235,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="pills-flush-cache" role="tabpanel" aria-labelledby="pills-flush-cache-tab" tabindex="0">
|
<div class="tab-pane fade" id="pills-flush-cache" role="tabpanel" aria-labelledby="pills-flush-cache-tab" tabindex="0">
|
||||||
<div class="row g-5 m-2">
|
<div class="row g-5 p-2">
|
||||||
<div class="col-md-7 col-lg-10">
|
<div class="col-md-7 col-lg-10">
|
||||||
<h4 class="mb-3">Flush a list of bad transaction IDs from cache</h4>
|
<h4 class="mb-3">Flush a list of bad transaction IDs from cache</h4>
|
||||||
<form class="needs-validation" novalidate="">
|
<form class="needs-validation" novalidate="">
|
||||||
|
@ -207,6 +262,7 @@
|
||||||
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" (click)="onFlushFromCache()">Flush Bad Txs</button>
|
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" (click)="onFlushFromCache()">Flush Bad Txs</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
import { AfterViewInit, Component, NgZone } from '@angular/core';
|
import { AfterViewInit, Component, NgZone, OnDestroy } from '@angular/core';
|
||||||
import { DaemonService } from '../../core/services/daemon/daemon.service';
|
import { DaemonService } from '../../core/services/daemon/daemon.service';
|
||||||
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
import { NavbarService } from '../../shared/components/navbar/navbar.service';
|
||||||
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
|
||||||
import { TxBacklogEntry } from '../../../common/TxBacklogEntry';
|
import { TxBacklogEntry } from '../../../common/TxBacklogEntry';
|
||||||
import { SimpleBootstrapCard } from '../../shared/utils';
|
import { SimpleBootstrapCard } from '../../shared/utils';
|
||||||
|
import { DaemonDataService } from '../../core/services';
|
||||||
|
import { table } from 'console';
|
||||||
|
import { SpentKeyImage, UnconfirmedTx } from '../../../common';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-transactions',
|
selector: 'app-transactions',
|
||||||
templateUrl: './transactions.component.html',
|
templateUrl: './transactions.component.html',
|
||||||
styleUrl: './transactions.component.scss'
|
styleUrl: './transactions.component.scss'
|
||||||
})
|
})
|
||||||
export class TransactionsComponent implements AfterViewInit {
|
export class TransactionsComponent implements AfterViewInit, OnDestroy {
|
||||||
public readonly navbarLinks: NavbarLink[];
|
public readonly navbarLinks: NavbarLink[];
|
||||||
|
|
||||||
public canRelay: boolean;
|
public canRelay: boolean;
|
||||||
|
@ -29,7 +33,13 @@ export class TransactionsComponent implements AfterViewInit {
|
||||||
return this.txIdsJsonString != '';
|
return this.txIdsJsonString != '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public daemonRunning: boolean = false;
|
public get daemonRunning(): boolean {
|
||||||
|
return this.daemonData.running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get daemonStopping(): boolean {
|
||||||
|
return this.daemonData.stopping;
|
||||||
|
}
|
||||||
|
|
||||||
public rawTxJsonString: string = '';
|
public rawTxJsonString: string = '';
|
||||||
public sendRawTxDoNotRelay: boolean = false;
|
public sendRawTxDoNotRelay: boolean = false;
|
||||||
|
@ -37,9 +47,28 @@ export class TransactionsComponent implements AfterViewInit {
|
||||||
public sendRawTxError: string = '';
|
public sendRawTxError: string = '';
|
||||||
public sendingRawTx: boolean = false;
|
public sendingRawTx: boolean = false;
|
||||||
|
|
||||||
constructor(private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
|
private get unconfirmedTxs(): UnconfirmedTx[] {
|
||||||
|
if (!this.daemonData.transactionPool) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.daemonData.transactionPool.transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get spentKeyImages(): SpentKeyImage[] {
|
||||||
|
if (!this.daemonData.transactionPool) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.daemonData.transactionPool.spentKeyImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
constructor(private daemonData: DaemonDataService, private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
|
||||||
this.navbarLinks = [
|
this.navbarLinks = [
|
||||||
new NavbarLink('pills-relay-tx-tab', '#pills-relay-tx', 'pills-relay-tx', true, 'Relay Tx'),
|
new NavbarLink('pills-tx-pool-tab', '#pills-tx-pool', 'pills-tx-pool', true, 'Pool'),
|
||||||
|
new NavbarLink('pills-relay-tx-tab', '#pills-relay-tx', 'pills-relay-tx', false, 'Relay Tx'),
|
||||||
new NavbarLink('pills-send-raw-tx-tab', '#pills-send-raw-tx', 'pills-send-raw-tx', false, 'Send Raw Tx'),
|
new NavbarLink('pills-send-raw-tx-tab', '#pills-send-raw-tx', 'pills-send-raw-tx', false, 'Send Raw Tx'),
|
||||||
new NavbarLink('pills-tx-backlog-tab', '#pills-tx-backlog', 'pills-tx-backlog', false, 'Tx Backlog'),
|
new NavbarLink('pills-tx-backlog-tab', '#pills-tx-backlog', 'pills-tx-backlog', false, 'Tx Backlog'),
|
||||||
new NavbarLink('pills-coinbase-tx-sum-tab', '#pills-coinbase-tx-sum', 'pills-coinbase-tx-sum', false, 'Coinbase Tx Sum'),
|
new NavbarLink('pills-coinbase-tx-sum-tab', '#pills-coinbase-tx-sum', 'pills-coinbase-tx-sum', false, 'Coinbase Tx Sum'),
|
||||||
|
@ -51,26 +80,63 @@ export class TransactionsComponent implements AfterViewInit {
|
||||||
this.txPoolBacklog = [];
|
this.txPoolBacklog = [];
|
||||||
|
|
||||||
this.canRelay = false;
|
this.canRelay = false;
|
||||||
this.daemonService.onDaemonStatusChanged.subscribe((running) => {
|
|
||||||
this.ngZone.run(() => {
|
|
||||||
this.daemonRunning = running;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.daemonService.isRunning().then((value: boolean) => {
|
|
||||||
this.ngZone.run(() => {
|
|
||||||
this.daemonRunning = value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
public ngAfterViewInit(): void {
|
||||||
|
this.ngZone.run(() => {
|
||||||
this.navbarService.setLinks(this.navbarLinks);
|
this.navbarService.setLinks(this.navbarLinks);
|
||||||
|
|
||||||
|
this.initTables();
|
||||||
|
this.subscriptions.push(this.daemonData.syncEnd.subscribe(() => {
|
||||||
|
this.refreshTables();
|
||||||
|
}));
|
||||||
|
|
||||||
this.load().then(() => {
|
this.load().then(() => {
|
||||||
this.navbarService.enableLinks();
|
this.navbarService.enableLinks();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.navbarService.disableLinks();
|
this.navbarService.disableLinks();
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.subscriptions.forEach((sub) => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriptions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private initTable(id: string): void {
|
||||||
|
const $table = $(`#${id}`);
|
||||||
|
|
||||||
|
$table.bootstrapTable({});
|
||||||
|
$table.bootstrapTable('refreshOptions', {
|
||||||
|
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private initTables(): void {
|
||||||
|
this.initTable('spentKeyImagesTable');
|
||||||
|
this.initTable('transactionsTable');
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadTransactionsTable(): void {
|
||||||
|
const $table = $('#transactionsTable');
|
||||||
|
|
||||||
|
$table.bootstrapTable('load', this.unconfirmedTxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadSpentKeyImagesTable(): void {
|
||||||
|
const $table = $('#spentKeyImagesTable');
|
||||||
|
|
||||||
|
$table.bootstrapTable('load', this.spentKeyImages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private refreshTables(): void {
|
||||||
|
this.loadSpentKeyImagesTable();
|
||||||
|
this.loadTransactionsTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async load(): Promise<void> {
|
private async load(): Promise<void> {
|
||||||
|
|
35
src/common/PeerInfo.ts
Normal file
35
src/common/PeerInfo.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
export class PeerInfo {
|
||||||
|
public readonly type: 'white' | 'gray';
|
||||||
|
public readonly host: number;
|
||||||
|
public readonly id: string;
|
||||||
|
public readonly ip: number;
|
||||||
|
public readonly lastSeen: number;
|
||||||
|
public readonly port: number;
|
||||||
|
|
||||||
|
constructor(type: 'white' | 'gray', host: number, id: string, ip: number, lastSeen: number, port: number) {
|
||||||
|
this.type = type;
|
||||||
|
this.host = host;
|
||||||
|
this.id = id;
|
||||||
|
this.ip = ip;
|
||||||
|
this.lastSeen = lastSeen;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static parse(peerInfo: any, type: 'white' | 'gray'): PeerInfo {
|
||||||
|
const host = peerInfo.host;
|
||||||
|
const id = peerInfo.id;
|
||||||
|
const ip = peerInfo.ip;
|
||||||
|
const lastSeen = peerInfo.last_seen;
|
||||||
|
const port = peerInfo.port;
|
||||||
|
|
||||||
|
return new PeerInfo(type, host, id, ip, lastSeen, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* host - unsigned int; IP address in integer format
|
||||||
|
id - string; Peer id
|
||||||
|
ip - unsigned int; IP address in integer format
|
||||||
|
last_seen - unsigned int; unix time at which the peer has been seen for the last time
|
||||||
|
port - unsigned int; TCP port the peer is using to connect to monero network.
|
||||||
|
*/
|
16
src/common/SpentKeyImage.ts
Normal file
16
src/common/SpentKeyImage.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export class SpentKeyImage {
|
||||||
|
public readonly idHash: string;
|
||||||
|
public readonly txsHashes: string[];
|
||||||
|
|
||||||
|
constructor(idHash: string, txsHashes: string[]) {
|
||||||
|
this.idHash = idHash;
|
||||||
|
this.txsHashes = txsHashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static parse(spentKeyImage: any): SpentKeyImage {
|
||||||
|
const idHash = spentKeyImage.id_hash;
|
||||||
|
const txsHashes = spentKeyImage.txs_hashes;
|
||||||
|
|
||||||
|
return new SpentKeyImage(idHash, txsHashes);
|
||||||
|
}
|
||||||
|
}
|
24
src/common/TxPool.ts
Normal file
24
src/common/TxPool.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { SpentKeyImage } from "./SpentKeyImage";
|
||||||
|
import { UnconfirmedTx } from "./UnconfirmedTx";
|
||||||
|
|
||||||
|
export class TxPool {
|
||||||
|
public readonly transactions: UnconfirmedTx[];
|
||||||
|
public readonly spentKeyImages: SpentKeyImage[];
|
||||||
|
|
||||||
|
constructor(transactions: UnconfirmedTx[], spentKeyImages: SpentKeyImage[]) {
|
||||||
|
this.transactions = transactions;
|
||||||
|
this.spentKeyImages = spentKeyImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static parse(txPool: any): TxPool {
|
||||||
|
const _transactions: any[] | undefined = txPool.transactions
|
||||||
|
const _spentKeyImages: any[] | undefined = txPool.spent_key_images;
|
||||||
|
const transactions: UnconfirmedTx[] = [];
|
||||||
|
const spentKeyImages: SpentKeyImage[] = [];
|
||||||
|
|
||||||
|
if (_transactions) _transactions.forEach((tx: any) => transactions.push(UnconfirmedTx.parse(tx)));
|
||||||
|
if (_spentKeyImages) _spentKeyImages.forEach((ski: any) => spentKeyImages.push(SpentKeyImage.parse(ski)));
|
||||||
|
|
||||||
|
return new TxPool(transactions, spentKeyImages);
|
||||||
|
}
|
||||||
|
}
|
90
src/common/UnconfirmedTx.ts
Normal file
90
src/common/UnconfirmedTx.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
|
||||||
|
export class UnconfirmedTx {
|
||||||
|
public readonly blobSize: number;
|
||||||
|
public readonly doNotRelay: boolean;
|
||||||
|
public readonly doubleSpendSeen: boolean;
|
||||||
|
public readonly fee: number;
|
||||||
|
public readonly idHash: string;
|
||||||
|
public readonly keptByBlock: boolean;
|
||||||
|
public readonly lastFailedHeight: number;
|
||||||
|
public readonly lastFailedIdHash: string;
|
||||||
|
public readonly lastRelayedTime: number;
|
||||||
|
public readonly maxUsedBlockHeight: number;
|
||||||
|
public readonly maxUsedBlockIdHash: string;
|
||||||
|
public readonly receiveTime: number;
|
||||||
|
public readonly relayed: boolean;
|
||||||
|
public readonly txBlob: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
blobSize: number,
|
||||||
|
doNotRelay: boolean,
|
||||||
|
doubleSpendSeen: boolean,
|
||||||
|
fee: number,
|
||||||
|
idHash: string,
|
||||||
|
keptByBlock: boolean,
|
||||||
|
lastFailedHeight: number,
|
||||||
|
lastFailedIdHash: string,
|
||||||
|
lastRelayedTime: number,
|
||||||
|
maxUsedBlockHeight: number,
|
||||||
|
maxUsedBlockIdHash: string,
|
||||||
|
receiveTime: number,
|
||||||
|
relayed: boolean,
|
||||||
|
txBlob: string
|
||||||
|
) {
|
||||||
|
this.blobSize = blobSize;
|
||||||
|
this.doNotRelay = doNotRelay;
|
||||||
|
this.doubleSpendSeen = doubleSpendSeen;
|
||||||
|
this.fee = fee;
|
||||||
|
this.idHash = idHash;
|
||||||
|
this.keptByBlock = keptByBlock;
|
||||||
|
this.lastFailedHeight = lastFailedHeight;
|
||||||
|
this.lastFailedIdHash = lastFailedIdHash;
|
||||||
|
this.lastRelayedTime = lastRelayedTime;
|
||||||
|
this.maxUsedBlockHeight = maxUsedBlockHeight;
|
||||||
|
this.maxUsedBlockIdHash = maxUsedBlockIdHash;
|
||||||
|
this.receiveTime = receiveTime;
|
||||||
|
this.relayed = relayed;
|
||||||
|
this.txBlob = txBlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static parse(unconfirmedTx: any): UnconfirmedTx {
|
||||||
|
const blobSize = unconfirmedTx.blob_size;
|
||||||
|
const doNotRelay = unconfirmedTx.do_not_relay;
|
||||||
|
const doubleSpendSeen = unconfirmedTx.double_spend_seen;
|
||||||
|
const fee = unconfirmedTx.fee;
|
||||||
|
const idHash = unconfirmedTx.id_hash;
|
||||||
|
const keptByBlock = unconfirmedTx.kept_by_block;
|
||||||
|
const lastFailedHeight = unconfirmedTx.last_failed_height;
|
||||||
|
const lastFailedIdHash = unconfirmedTx.last_failed_id_hash;
|
||||||
|
const lastRelayedTime = unconfirmedTx.last_relayed_time;
|
||||||
|
const maxUsedBlockHeight = unconfirmedTx.max_used_block_height;
|
||||||
|
const maxUsedBlockIdHash = unconfirmedTx.max_used_block_id_hash;
|
||||||
|
const receiveTime = unconfirmedTx.receive_time;
|
||||||
|
const relayed = unconfirmedTx.relayed;
|
||||||
|
const txBlob = unconfirmedTx.tx_blob;
|
||||||
|
|
||||||
|
return new UnconfirmedTx(
|
||||||
|
blobSize, doNotRelay, doubleSpendSeen, fee, idHash, keptByBlock,
|
||||||
|
lastFailedHeight, lastFailedIdHash, lastRelayedTime, maxUsedBlockHeight,
|
||||||
|
maxUsedBlockIdHash, receiveTime, relayed, txBlob
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* blob_size - unsigned int; The size of the full transaction blob.
|
||||||
|
do_not_relay; boolean; States if this transaction should not be relayed
|
||||||
|
double_spend_seen - boolean; States if this transaction has been seen as double spend.
|
||||||
|
fee - unsigned int; The amount of the mining fee included in the transaction, in atomic units.
|
||||||
|
id_hash - string; The transaction ID hash.
|
||||||
|
kept_by_block - boolean; States if the tx was included in a block at least once (true) or not (false).
|
||||||
|
last_failed_height - unsigned int; If the transaction validation has previously failed, this tells at what height that occurred.
|
||||||
|
last_failed_id_hash - string; Like the previous, this tells the previous transaction ID hash.
|
||||||
|
last_relayed_time - unsigned int; Last unix time at which the transaction has been relayed.
|
||||||
|
max_used_block_height - unsigned int; Tells the height of the most recent block with an output used in this transaction.
|
||||||
|
max_used_block_id_hash - string; Tells the hash of the most recent block with an output used in this transaction.
|
||||||
|
receive_time - unsigned int; The Unix time that the transaction was first seen on the network by the node.
|
||||||
|
relayed - boolean; States if this transaction has been relayed
|
||||||
|
tx_blob - unsigned int; Hexadecimal blob representing the transaction.
|
||||||
|
*/
|
|
@ -26,6 +26,7 @@ export { OutKey } from './OutKey';
|
||||||
export { Output } from './Output';
|
export { Output } from './Output';
|
||||||
export { OutputDistribution } from './OutputDistribution';
|
export { OutputDistribution } from './OutputDistribution';
|
||||||
export { Peer } from './Peer';
|
export { Peer } from './Peer';
|
||||||
|
export { PeerInfo } from './PeerInfo';
|
||||||
export { PublicNode } from './PublicNode';
|
export { PublicNode } from './PublicNode';
|
||||||
export { Span } from './Span';
|
export { Span } from './Span';
|
||||||
export { SyncInfo } from './SyncInfo';
|
export { SyncInfo } from './SyncInfo';
|
||||||
|
@ -34,6 +35,9 @@ export { TxInfo } from './TxInfo';
|
||||||
export { UpdateInfo } from './UpdateInfo';
|
export { UpdateInfo } from './UpdateInfo';
|
||||||
export { LogCategoryLevel, LogCategories } from './LogCategories';
|
export { LogCategoryLevel, LogCategories } from './LogCategories';
|
||||||
export { NetStatsHistory, NetStatsHistoryEntry } from './NetStatsHistory';
|
export { NetStatsHistory, NetStatsHistoryEntry } from './NetStatsHistory';
|
||||||
|
export { UnconfirmedTx } from './UnconfirmedTx';
|
||||||
|
export { SpentKeyImage } from './SpentKeyImage';
|
||||||
|
export { TxPool } from './TxPool';
|
||||||
|
|
||||||
export * from './error';
|
export * from './error';
|
||||||
export * from './request';
|
export * from './request';
|
||||||
|
|
11
src/common/request/GetPeerListRequest.ts
Normal file
11
src/common/request/GetPeerListRequest.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { RPCRequest } from "./RPCRequest";
|
||||||
|
|
||||||
|
export class GetPeerListRequest extends RPCRequest {
|
||||||
|
public override readonly method: 'get_peer_list' = 'get_peer_list';
|
||||||
|
public override readonly restricted: boolean = true;
|
||||||
|
|
||||||
|
public override toDictionary(): { [key: string]: any; } {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/common/request/GetTransactionPoolRequest.ts
Normal file
11
src/common/request/GetTransactionPoolRequest.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { RPCRequest } from "./RPCRequest";
|
||||||
|
|
||||||
|
export class GetTransactionPoolRequest extends RPCRequest {
|
||||||
|
public override readonly method: 'get_transaction_pool' = 'get_transaction_pool';
|
||||||
|
public override readonly restricted: boolean = false;
|
||||||
|
|
||||||
|
public override toDictionary(): { [key: string]: any; } {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -57,6 +57,8 @@ export { SetBootstrapDaemonRequest } from "./SetBootstrapDaemonRequest";
|
||||||
export { SetLogLevelRequest } from "./SetLogLevelRequest";
|
export { SetLogLevelRequest } from "./SetLogLevelRequest";
|
||||||
export { SetLogHashRateRequest } from "./SetLogHashRateRequest";
|
export { SetLogHashRateRequest } from "./SetLogHashRateRequest";
|
||||||
export { SetLogCategoriesRequest } from "./SetLogCategoriesRequest";
|
export { SetLogCategoriesRequest } from "./SetLogCategoriesRequest";
|
||||||
|
export { GetTransactionPoolRequest } from "./GetTransactionPoolRequest";
|
||||||
|
export { GetPeerListRequest } from "./GetPeerListRequest";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restricted requests
|
* Restricted requests
|
||||||
|
|
Loading…
Reference in a new issue