mirror of
https://github.com/everoddandeven/monerod-gui.git
synced 2024-12-22 11:39:25 +00:00
Wide implementation
This commit is contained in:
parent
f8230e9cc5
commit
334a124a4a
92 changed files with 2757 additions and 66 deletions
BIN
.nx/cache/18.3.4-nx.linux-x64-gnu.node
vendored
Normal file
BIN
.nx/cache/18.3.4-nx.linux-x64-gnu.node
vendored
Normal file
Binary file not shown.
15
angular.json
15
angular.json
|
@ -3,7 +3,8 @@
|
|||
"cli": {
|
||||
"schematicCollections": [
|
||||
"@angular-eslint/schematics"
|
||||
]
|
||||
],
|
||||
"analytics": false
|
||||
},
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
|
@ -36,9 +37,15 @@
|
|||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
"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"
|
||||
],
|
||||
"scripts": [],
|
||||
"customWebpackConfig": {
|
||||
"path": "./angular.webpack.js",
|
||||
"replaceDuplicatePlugins": true
|
||||
|
@ -142,7 +149,7 @@
|
|||
"options": {
|
||||
"polyfills": ["src/polyfills-test.ts"],
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"globalMocks": ["styleTransform", "matchMedia", "getComputedStyle"],
|
||||
"globalMocks": ["styleTransform", "matchMedia", "getComputedStyle"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
|
|
85
package-lock.json
generated
85
package-lock.json
generated
|
@ -17,6 +17,10 @@
|
|||
"@angular/platform-browser": "17.3.6",
|
||||
"@angular/platform-browser-dynamic": "17.3.6",
|
||||
"@angular/router": "17.3.6",
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap-icons": "1.11.3",
|
||||
"bootstrap-table": "1.23.2",
|
||||
"jquery": "3.7.1",
|
||||
"rxjs": "7.8.1",
|
||||
"tslib": "2.6.2",
|
||||
"zone.js": "0.14.4"
|
||||
|
@ -35,7 +39,9 @@
|
|||
"@ngx-translate/core": "15.0.0",
|
||||
"@ngx-translate/http-loader": "8.0.0",
|
||||
"@playwright/test": "1.43.1",
|
||||
"@types/bootstrap": "5.2.10",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/jquery": "3.5.30",
|
||||
"@types/node": "20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
||||
"@typescript-eslint/parser": "7.7.1",
|
||||
|
@ -5050,6 +5056,15 @@
|
|||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.0.tgz",
|
||||
|
@ -5530,6 +5545,15 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bootstrap": {
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz",
|
||||
"integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cacheable-request": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
|
||||
|
@ -5693,6 +5717,15 @@
|
|||
"pretty-format": "^29.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jquery": {
|
||||
"version": "3.5.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.30.tgz",
|
||||
"integrity": "sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/sizzle": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jsdom": {
|
||||
"version": "20.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
|
||||
|
@ -5835,6 +5868,12 @@
|
|||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sizzle": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz",
|
||||
"integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/sockjs": {
|
||||
"version": "0.3.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
|
||||
|
@ -7849,6 +7888,47 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.11.8"
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap-icons": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
|
||||
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/bootstrap-table": {
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.2.tgz",
|
||||
"integrity": "sha512-1IFiWFZzbKlleXgYEHdwHkX6rxlQMEx2N1tA8rJK/j08pI+NjIGnxFeXUL26yQLQ0U135eis/BX3OV1+anY25g==",
|
||||
"peerDependencies": {
|
||||
"jquery": "3"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
|
@ -16200,6 +16280,11 @@
|
|||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
"@angular/platform-browser": "17.3.6",
|
||||
"@angular/platform-browser-dynamic": "17.3.6",
|
||||
"@angular/router": "17.3.6",
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap-icons": "1.11.3",
|
||||
"bootstrap-table": "1.23.2",
|
||||
"jquery": "3.7.1",
|
||||
"rxjs": "7.8.1",
|
||||
"tslib": "2.6.2",
|
||||
"zone.js": "0.14.4"
|
||||
|
@ -72,7 +76,9 @@
|
|||
"@ngx-translate/core": "15.0.0",
|
||||
"@ngx-translate/http-loader": "8.0.0",
|
||||
"@playwright/test": "1.43.1",
|
||||
"@types/bootstrap": "5.2.10",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/jquery": "3.5.30",
|
||||
"@types/node": "20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
||||
"@typescript-eslint/parser": "7.7.1",
|
||||
|
|
|
@ -4,11 +4,12 @@ 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';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'home',
|
||||
redirectTo: 'detail',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
|
@ -21,7 +22,8 @@ const routes: Routes = [
|
|||
imports: [
|
||||
RouterModule.forRoot(routes, {}),
|
||||
HomeRoutingModule,
|
||||
DetailRoutingModule
|
||||
DetailRoutingModule,
|
||||
HardForkInfoRoutingModule
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
<router-outlet></router-outlet>
|
||||
<app-navbar></app-navbar>
|
||||
<div class="d-flex">
|
||||
<app-sidebar></app-sidebar>
|
||||
<div class="d-flex">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:host {
|
||||
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #373636;
|
||||
}
|
|
@ -15,6 +15,10 @@ import { HomeModule } from './home/home.module';
|
|||
import { DetailModule } from './detail/detail.module';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { SidebarComponent } from "./sidebar/sidebar.component";
|
||||
import { BansModule } from './bans/bans.module';
|
||||
import { NavbarComponent } from "./navbar/navbar.component";
|
||||
import { MiningModule } from './mining/mining.module';
|
||||
|
||||
// AoT requires an exported function for factories
|
||||
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||
|
@ -29,15 +33,19 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
|
|||
SharedModule,
|
||||
HomeModule,
|
||||
DetailModule,
|
||||
BansModule,
|
||||
MiningModule,
|
||||
AppRoutingModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: httpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
})
|
||||
],
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: httpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
SidebarComponent,
|
||||
NavbarComponent
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
|
14
src/app/bans/bans-routing.module.ts
Normal file
14
src/app/bans/bans-routing.module.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { BansComponent } from './bans.component';
|
||||
|
||||
const routes: Routes = [{
|
||||
path: 'bans',
|
||||
component: BansComponent
|
||||
}];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class BansRoutingModule { }
|
14
src/app/bans/bans.component.html
Normal file
14
src/app/bans/bans.component.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<table
|
||||
id="bansTable"
|
||||
data-toggle="bansTable"
|
||||
data-toolbar="#toolbar"
|
||||
data-height="460"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field="host">Host</th>
|
||||
<th data-field="ip">Ip</th>
|
||||
<th data-field="seconds">Seconds</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
0
src/app/bans/bans.component.scss
Normal file
0
src/app/bans/bans.component.scss
Normal file
23
src/app/bans/bans.component.spec.ts
Normal file
23
src/app/bans/bans.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BansComponent } from './bans.component';
|
||||
|
||||
describe('BansComponent', () => {
|
||||
let component: BansComponent;
|
||||
let fixture: ComponentFixture<BansComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [BansComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(BansComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
56
src/app/bans/bans.component.ts
Normal file
56
src/app/bans/bans.component.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { AfterViewInit, Component } from '@angular/core';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { NavbarService } from '../navbar/navbar.service';
|
||||
import { DaemonService } from '../core/services/daemon/daemon.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bans',
|
||||
templateUrl: './bans.component.html',
|
||||
styleUrl: './bans.component.scss'
|
||||
})
|
||||
export class BansComponent implements AfterViewInit {
|
||||
|
||||
constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService) {
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
if (event.url != '/bans') return;
|
||||
this.onNavigationEnd();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
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 {
|
||||
this.navbarService.removeNavbarLinks();
|
||||
}
|
||||
|
||||
private async load(): Promise<void> {
|
||||
const $table = $('#bansTable');
|
||||
|
||||
const _bans = await this.daemonService.getBans();
|
||||
const bans: any[] = [];
|
||||
|
||||
_bans.forEach((ban) => bans.push({
|
||||
'ip': ban.ip,
|
||||
'host': ban.host,
|
||||
'seconds': ban.seconds
|
||||
}));
|
||||
|
||||
$table.bootstrapTable('load', bans);
|
||||
|
||||
}
|
||||
|
||||
}
|
19
src/app/bans/bans.module.ts
Normal file
19
src/app/bans/bans.module.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { BansRoutingModule } from './bans-routing.module';
|
||||
import { BansComponent } from './bans.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
BansComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
BansRoutingModule
|
||||
]
|
||||
})
|
||||
export class BansModule { }
|
16
src/app/core/services/daemon/daemon.service.spec.ts
Normal file
16
src/app/core/services/daemon/daemon.service.spec.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DaemonService } from './daemon.service';
|
||||
|
||||
describe('DaemonService', () => {
|
||||
let service: DaemonService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(DaemonService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
256
src/app/core/services/daemon/daemon.service.ts
Normal file
256
src/app/core/services/daemon/daemon.service.ts
Normal file
|
@ -0,0 +1,256 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BlockCount } from '../../../../common/BlockCount';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import {
|
||||
GetBlockCountRequest, GetBlockHashRequest, GetBlockTemplateRequest, JsonRPCRequest,
|
||||
SubmitBlockRequest, GenerateBlocksRequest, GetLastBlockHeaderRequest,
|
||||
GetBlockHeaderByHashRequest, GetBlockHeaderByHeightRequest, GetBlockHeadersRangeRequest,
|
||||
GetConnectionsRequest, GetInfoRequest, HardForkInfoRequest, SetBansRequest, GetBansRequest,
|
||||
BannedRequest, FlushTxPoolRequest, GetOutputHistogramRequest,
|
||||
SyncInfoRequest,
|
||||
GetVersionRequest,
|
||||
GetFeeEstimateRequest,
|
||||
GetAlternateChainsRequest,
|
||||
GetTxPoolBacklogRequest,
|
||||
PruneBlockchainRequest,
|
||||
CalculatePoWHashRequest,
|
||||
FlushCacheRequest,
|
||||
GetMinerDataRequest
|
||||
} from '../../../../common/request';
|
||||
import { BlockTemplate } from '../../../../common/BlockTemplate';
|
||||
import { GeneratedBlocks } from '../../../../common/GeneratedBlocks';
|
||||
import { BlockHeader } from '../../../../common/BlockHeader';
|
||||
import { Connection } from '../../../../common/Connection';
|
||||
import { DaemonInfo } from '../../../../common/DaemonInfo';
|
||||
import { HardForkInfo } from '../../../../common/HardForkInfo';
|
||||
import { Ban } from '../../../../common/Ban';
|
||||
import { HistogramEntry } from '../../../../common/HistogramEntry';
|
||||
import { SyncInfo } from '../../../../common/SyncInfo';
|
||||
import { DaemonVersion } from '../../../../common/DaemonVersion';
|
||||
import { FeeEstimate } from '../../../../common/FeeEstimate';
|
||||
import { Chain } from '../../../../common/Chain';
|
||||
import { RelayTxRequest } from '../../../../common/request/RelayTxRequest';
|
||||
import { TxBacklogEntry } from '../../../../common/TxBacklogEntry';
|
||||
import { BlockchainPruneInfo } from '../../../../common/BlockchainPruneInfo';
|
||||
import { MinerData } from '../../../../common/MinerData';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DaemonService {
|
||||
private url: string = "http://127.0.0.1:28081";
|
||||
|
||||
private readonly headers: { [key: string]: string } = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
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 getBlockCount(): Promise<BlockCount> {
|
||||
const response = await this.callJsonRpc(new GetBlockCountRequest());
|
||||
|
||||
return BlockCount.parse(response.result);
|
||||
}
|
||||
|
||||
public async getBlockHash(blockHeight: number): Promise<string> {
|
||||
const response = await this.callJsonRpc(new GetBlockHashRequest(blockHeight));
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
public async getBlockTemplate(walletAddress: string, reserveSize: number) {
|
||||
const response = await this.callJsonRpc(new GetBlockTemplateRequest(walletAddress, reserveSize));
|
||||
|
||||
return BlockTemplate.parse(response.result);
|
||||
}
|
||||
|
||||
public async submitBlock(... blockBlobData: string[]): Promise<void> {
|
||||
const response = await this.callJsonRpc(new SubmitBlockRequest(blockBlobData));
|
||||
|
||||
if (response.error) {
|
||||
if (!response.message) {
|
||||
throw new Error(`Error code: ${response.code}`);
|
||||
}
|
||||
|
||||
throw new Error(response.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async generateBlocks(amountOfBlocks: number, walletAddress: string, prevBlock: string = '', startingNonce: number): Promise<GeneratedBlocks> {
|
||||
const response = await this.callJsonRpc(new GenerateBlocksRequest(amountOfBlocks, walletAddress, prevBlock, startingNonce));
|
||||
|
||||
return GeneratedBlocks.parse(response.result);
|
||||
}
|
||||
|
||||
public async getLastBlockHeader(fillPowHash: boolean = false): Promise<BlockHeader> {
|
||||
const response = await this.callJsonRpc(new GetLastBlockHeaderRequest(fillPowHash));
|
||||
|
||||
return BlockHeader.parse(response.block_header);
|
||||
}
|
||||
|
||||
public async getBlockHeaderByHash(hash: string, fillPowHash: boolean = false): Promise<BlockHeader> {
|
||||
const response = await this.callJsonRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash));
|
||||
|
||||
return BlockHeader.parse(response.block_header);
|
||||
}
|
||||
|
||||
public async getBlockHeaderByHeight(height: number, fillPowHash: boolean = false): Promise<BlockHeader> {
|
||||
const response = await this.callJsonRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash));
|
||||
|
||||
return BlockHeader.parse(response.block_header);
|
||||
}
|
||||
|
||||
public async getBlockHeadersRange(startHeight: number, endHeight: number, fillPowHash: boolean = false): Promise<BlockHeader[]> {
|
||||
const response = await this.callJsonRpc(new GetBlockHeadersRangeRequest(startHeight, endHeight, fillPowHash));
|
||||
const block_headers: any[] = response.block_headers;
|
||||
const result: BlockHeader[] = [];
|
||||
|
||||
block_headers.forEach((block_header: any) => result.push(BlockHeader.parse(block_header)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async getConnections(): Promise<Connection[]> {
|
||||
const response = await this.callJsonRpc(new GetConnectionsRequest());
|
||||
const connections: any[] = response.connections;
|
||||
const result: Connection[] = [];
|
||||
|
||||
connections.forEach((connection: any) => result.push(Connection.parse(connection)))
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async getInfo(): Promise<DaemonInfo> {
|
||||
const response = await this.callJsonRpc(new GetInfoRequest());
|
||||
|
||||
return DaemonInfo.parse(response.result);
|
||||
}
|
||||
|
||||
public async hardForkInfo(): Promise<HardForkInfo> {
|
||||
const response = await this.callJsonRpc(new HardForkInfoRequest());
|
||||
|
||||
return HardForkInfo.parse(response.result);
|
||||
}
|
||||
|
||||
public async setBans(...bans: Ban[]) {
|
||||
const response = await this.callJsonRpc(new SetBansRequest(bans));
|
||||
|
||||
if (response.status != 'OK') {
|
||||
throw new Error(`Error code: ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getBans(): Promise<Ban[]> {
|
||||
const response = await this.callJsonRpc(new GetBansRequest());
|
||||
const bans: any[] = response.bans;
|
||||
const result: Ban[] = [];
|
||||
|
||||
bans.forEach((ban: any) => result.push(Ban.parse(ban)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async banned(address: string): Promise<Ban> {
|
||||
const response = await this.callJsonRpc(new BannedRequest(address));
|
||||
const result = response.result;
|
||||
|
||||
if (result.status != 'OK') {
|
||||
throw new Error(`Error code: ${result.response}`);
|
||||
}
|
||||
|
||||
return new Ban(address, 0, result.banned, result.seconds);
|
||||
}
|
||||
|
||||
public async flushTxPool(... txIds: string[]): Promise<void> {
|
||||
const response = await this.callJsonRpc(new FlushTxPoolRequest(txIds));
|
||||
|
||||
if (response.status != 'OK') {
|
||||
throw new Error(`Error code: ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getOutputHistogram(amounts: number[], minCount: number, maxCount: number, unlocked: boolean, recentCutoff: number): Promise<HistogramEntry[]> {
|
||||
const response = await this.callJsonRpc(new GetOutputHistogramRequest(amounts, minCount, maxCount, unlocked, recentCutoff));
|
||||
const entries: any[] = response.histogram;
|
||||
const result: HistogramEntry[] = [];
|
||||
|
||||
entries.forEach((entry: any) => result.push(HistogramEntry.parse(entry)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async syncInfo(): Promise<SyncInfo> {
|
||||
const response = await this.callJsonRpc(new SyncInfoRequest());
|
||||
|
||||
return SyncInfo.parse(response.result);
|
||||
}
|
||||
|
||||
public async getVersion(): Promise<DaemonVersion> {
|
||||
const response = await this.callJsonRpc(new GetVersionRequest());
|
||||
|
||||
return DaemonVersion.parse(response.result);
|
||||
}
|
||||
|
||||
public async getFeeEstimate(): Promise<FeeEstimate> {
|
||||
const response = await this.callJsonRpc(new GetFeeEstimateRequest());
|
||||
|
||||
return FeeEstimate.parse(response.result);
|
||||
}
|
||||
|
||||
public async getAlternateChains(): Promise<Chain[]> {
|
||||
const response = await this.callJsonRpc(new GetAlternateChainsRequest());
|
||||
const chains: any[] = response.result.chains ? response.result.chains : [];
|
||||
const result: Chain[] = [];
|
||||
|
||||
chains.forEach((chain: any) => result.push(Chain.parse(chain)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async relayTx(... txIds: string[]): Promise<void> {
|
||||
const response = await this.callJsonRpc(new RelayTxRequest(txIds));
|
||||
|
||||
if (response.result.status != 'OK') {
|
||||
throw new Error(`Error code: ${response.result.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getTxPoolBacklog(): Promise<TxBacklogEntry[]> {
|
||||
const response = await this.callJsonRpc(new GetTxPoolBacklogRequest());
|
||||
|
||||
return TxBacklogEntry.fromBinary(response.backlog);
|
||||
}
|
||||
|
||||
public async pruneBlockchain(check: boolean = false): Promise<BlockchainPruneInfo> {
|
||||
const response = await this.callJsonRpc(new PruneBlockchainRequest(check));
|
||||
|
||||
return BlockchainPruneInfo.parse(response.result);
|
||||
}
|
||||
|
||||
public async calculatePoWHash(majorVersion: number, height: number, blockBlob: string, seedHash: string): Promise<string> {
|
||||
const response = await this.callJsonRpc(new CalculatePoWHashRequest(majorVersion, height, blockBlob, seedHash));
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
public async flushCache(badTxs: boolean = false, badBlocks: boolean = false): Promise<void> {
|
||||
const response = await this.callJsonRpc(new FlushCacheRequest(badTxs, badBlocks));
|
||||
|
||||
if(response.result.status != 'OK') {
|
||||
throw new Error(`Error code: ${response.result.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async getMinerData(): Promise<MinerData> {
|
||||
const response = await this.callJsonRpc(new GetMinerDataRequest());
|
||||
|
||||
return MinerData.parse(response.result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,39 @@
|
|||
<div class="container">
|
||||
<h1 class="title">
|
||||
{{ 'PAGES.DETAIL.TITLE' | translate }}
|
||||
</h1>
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" tabindex="0">
|
||||
<div class="row d-flex">
|
||||
|
||||
<a routerLink="/">{{ 'PAGES.DETAIL.BACK_TO_HOME' | translate }}</a>
|
||||
</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>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{card.content}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pills-profile" role="tabpanel" aria-labelledby="pills-profile-tab" tabindex="0">
|
||||
<div class="m-3">
|
||||
<table
|
||||
id="table"
|
||||
data-toggle="table"
|
||||
data-toolbar="#toolbar"
|
||||
data-height="460"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field="address">Remote Host</th>
|
||||
<th data-field="peerId">Peer ID</th>
|
||||
<th data-field="height">Height</th>
|
||||
<th data-field="pruningSeed">Prune Seed</th>
|
||||
<th data-field="state">State</th>
|
||||
<th data-field="currentDownload">Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pills-contact" role="tabpanel" aria-labelledby="pills-contact-tab" tabindex="0">Spans</div>
|
||||
<div class="tab-pane fade" id="pills-disabled" role="tabpanel" aria-labelledby="pills-disabled-tab" tabindex="0">...</div>
|
||||
</div>
|
|
@ -1,16 +1,196 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, AfterViewInit } 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';
|
||||
import { NavbarService } from '../navbar/navbar.service';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { DaemonInfo } from '../../common/DaemonInfo';
|
||||
|
||||
@Component({
|
||||
selector: 'app-detail',
|
||||
templateUrl: './detail.component.html',
|
||||
styleUrls: ['./detail.component.scss']
|
||||
})
|
||||
export class DetailComponent implements OnInit {
|
||||
export class DetailComponent implements OnInit, AfterViewInit {
|
||||
|
||||
constructor() { }
|
||||
private syncInfo?: SyncInfo;
|
||||
private daemonInfo?: DaemonInfo;
|
||||
private readonly navbarLinks: NavbarLink[];
|
||||
|
||||
private syncStatus: string;
|
||||
private height: number;
|
||||
private targetHeight: number;
|
||||
private nextNeededPruningSeed: number;
|
||||
private overview: string;
|
||||
private blockCount: number;
|
||||
private version: string;
|
||||
|
||||
private blockchainSize: string;
|
||||
private diskUsage: string;
|
||||
private networkType: string;
|
||||
private connectionStatus: string;
|
||||
private txCount: number;
|
||||
private poolSize: number;
|
||||
private nodeType: string;
|
||||
private syncProgress: string;
|
||||
|
||||
public cards: Card[];
|
||||
|
||||
constructor(private router: Router,private daemonService: DaemonService, private navbarService: NavbarService) {
|
||||
this.syncStatus = 'Not synced';
|
||||
this.height = 0;
|
||||
this.targetHeight = 0;
|
||||
this.nextNeededPruningSeed = 0;
|
||||
this.overview = '';
|
||||
this.blockCount = 0;
|
||||
this.version = '';
|
||||
this.diskUsage = '0 %';
|
||||
this.networkType = '';
|
||||
this.connectionStatus = 'offline';
|
||||
this.txCount = 0;
|
||||
this.poolSize = 0;
|
||||
this.nodeType = 'unknown';
|
||||
this.blockchainSize = '0 GB';
|
||||
this.syncProgress = '0 %';
|
||||
|
||||
this.navbarLinks = [
|
||||
new NavbarLink('pills-home-tab', '#pills-home', 'pills-home', true, 'Overview'),
|
||||
new NavbarLink('pills-profile-tab', '#pills-profile', 'pills-profile', false, 'Peers')
|
||||
];
|
||||
|
||||
this.cards = [];
|
||||
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
if (event.url != '/detail') return;
|
||||
this.onNavigationEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log('DetailComponent INIT');
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
console.log('DetailComponent AFTER VIEW INIT');
|
||||
|
||||
setTimeout(() => {
|
||||
const $table = $('#table');
|
||||
$table.bootstrapTable({});
|
||||
$table.bootstrapTable('refreshOptions', {
|
||||
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||
});
|
||||
this.Load();
|
||||
|
||||
}, 500);
|
||||
}
|
||||
|
||||
private onNavigationEnd(): void {
|
||||
this.Load().then(() => {
|
||||
this.cards = this.createCards();
|
||||
this.navbarService.setNavbarLinks(this.navbarLinks);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private createCards(): Card[] {
|
||||
return [
|
||||
new Card('Connection Status', this.connectionStatus),
|
||||
new Card('Network Type', this.networkType),
|
||||
new Card('Node Type', this.nodeType),
|
||||
new Card('Sync progress', this.syncProgress),
|
||||
new Card('Scan Height', `${this.height} / ${this.targetHeight}`),
|
||||
new Card('Next needed pruning seed', `${this.nextNeededPruningSeed}`),
|
||||
new Card('Block count', `${this.blockCount}`),
|
||||
new Card('Monero version', this.version),
|
||||
new Card('Blockchain size', this.blockchainSize),
|
||||
new Card('Disk usage', this.diskUsage),
|
||||
new Card('Transaction count', `${this.txCount}`),
|
||||
new Card('Pool size', `${this.poolSize}`)
|
||||
];
|
||||
}
|
||||
|
||||
public async Load(): Promise<void> {
|
||||
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());
|
||||
}
|
||||
|
||||
public async isBlockchainPruned(): Promise<boolean> {
|
||||
const result = await this.daemonService.pruneBlockchain(true);
|
||||
|
||||
return result.pruned;
|
||||
}
|
||||
|
||||
public getPeers(): any[] {
|
||||
if (!this.syncInfo) return [];
|
||||
|
||||
const peers: any[] = [];
|
||||
|
||||
this.syncInfo.peers.forEach((peer: Peer) => peers.push({
|
||||
'address': peer.info.address,
|
||||
'peerId': peer.info.peerId,
|
||||
'height': peer.info.height,
|
||||
'pruningSeed': peer.info.pruningSeed,
|
||||
'state': peer.info.state,
|
||||
'currentDownload': `${peer.info.currentDownload / 1000} kB/s`
|
||||
}));
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Card {
|
||||
public header: string;
|
||||
public content: string;
|
||||
|
||||
constructor(header: string, content: string) {
|
||||
this.header = header;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
14
src/app/hard-fork-info/hard-fork-info-routing.module.ts
Normal file
14
src/app/hard-fork-info/hard-fork-info-routing.module.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { HardForkInfoComponent } from './hard-fork-info.component';
|
||||
|
||||
const routes: Routes = [{
|
||||
path: 'hardforkinfo',
|
||||
component: HardForkInfoComponent
|
||||
}];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class HardForkInfoRoutingModule { }
|
10
src/app/hard-fork-info/hard-fork-info.component.html
Normal file
10
src/app/hard-fork-info/hard-fork-info.component.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div class="row d-flex">
|
||||
@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>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{card.content}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
0
src/app/hard-fork-info/hard-fork-info.component.scss
Normal file
0
src/app/hard-fork-info/hard-fork-info.component.scss
Normal file
23
src/app/hard-fork-info/hard-fork-info.component.spec.ts
Normal file
23
src/app/hard-fork-info/hard-fork-info.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HardForkInfoComponent } from './hard-fork-info.component';
|
||||
|
||||
describe('HardForkInfoComponent', () => {
|
||||
let component: HardForkInfoComponent;
|
||||
let fixture: ComponentFixture<HardForkInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HardForkInfoComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HardForkInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
80
src/app/hard-fork-info/hard-fork-info.component.ts
Normal file
80
src/app/hard-fork-info/hard-fork-info.component.ts
Normal file
|
@ -0,0 +1,80 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { DaemonService } from '../core/services/daemon/daemon.service';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { NavbarService } from '../navbar/navbar.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hard-fork-info',
|
||||
templateUrl: './hard-fork-info.component.html',
|
||||
styleUrl: './hard-fork-info.component.scss'
|
||||
})
|
||||
export class HardForkInfoComponent {
|
||||
public cards: Card[];
|
||||
private earliestHeight: number;
|
||||
private enabled: boolean;
|
||||
private threshold: number;
|
||||
private blockVersion: number;
|
||||
private votes: number;
|
||||
private voting: number;
|
||||
private window: number;
|
||||
|
||||
constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService) {
|
||||
this.cards = [];
|
||||
this.enabled = false;
|
||||
this.earliestHeight = 0;
|
||||
this.threshold = 0;
|
||||
this.blockVersion = 0;
|
||||
this.votes = 0;
|
||||
this.voting = 0;
|
||||
this.window = 0;
|
||||
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
if (event.url != '/hardforkinfo') return;
|
||||
this.onNavigationEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onNavigationEnd(): void {
|
||||
this.load().then(() => {
|
||||
this.cards = this.createCards();
|
||||
this.navbarService.removeNavbarLinks();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private async load(): Promise<void> {
|
||||
const info = await this.daemonService.hardForkInfo();
|
||||
|
||||
this.earliestHeight = info.earliestHeight;
|
||||
this.threshold = info.threshold;
|
||||
this.blockVersion = info.version;
|
||||
this.votes = info.votes;
|
||||
this.voting = info.voting;
|
||||
this.window = info.window;
|
||||
}
|
||||
|
||||
private createCards(): Card[] {
|
||||
return [
|
||||
new Card('Status', this.enabled ? 'enabled' : 'disabled'),
|
||||
new Card('Earliest height', `${this.earliestHeight}`),
|
||||
new Card('Threshold', `${this.threshold}`),
|
||||
new Card('Block version', `${this.blockVersion}`),
|
||||
new Card('Votes', `${this.votes}`),
|
||||
new Card('Voting', `${this.voting}`),
|
||||
new Card('Window', `${this.window}`)
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Card {
|
||||
public header: string;
|
||||
public content: string;
|
||||
|
||||
constructor(header: string, content: string) {
|
||||
this.header = header;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
14
src/app/hard-fork-info/hard-fork-info.module.ts
Normal file
14
src/app/hard-fork-info/hard-fork-info.module.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { HardForkInfoRoutingModule } from './hard-fork-info-routing.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HardForkInfoRoutingModule
|
||||
]
|
||||
})
|
||||
export class HardForkInfoModule { }
|
14
src/app/mining/mining-routing.module.ts
Normal file
14
src/app/mining/mining-routing.module.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { MiningComponent } from './mining.component';
|
||||
|
||||
const routes: Routes = [{
|
||||
path: 'mining',
|
||||
component: MiningComponent
|
||||
}];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class MiningRoutingModule { }
|
53
src/app/mining/mining.component.html
Normal file
53
src/app/mining/mining.component.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<div class="tab-content" id="pills-tabContent">
|
||||
<div class="tab-pane fade show active" id="pills-miner-data" role="tabpanel" aria-labelledby="pills-miner-data-tab" tabindex="0">
|
||||
<div class="row d-flex">
|
||||
|
||||
@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>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{card.content}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pills-alternate-chains" role="tabpanel" aria-labelledby="pills-alternate-chains-tab" tabindex="0">
|
||||
<div class="m-3">
|
||||
<table
|
||||
id="chainsTable"
|
||||
data-toggle="chainsTable"
|
||||
data-toolbar="#toolbar"
|
||||
data-height="460"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field="blockHash">Block Hash</th>
|
||||
<th data-field="height">Height</th>
|
||||
<th data-field="length">Length</th>
|
||||
<th data-field="mainChainParentBlock">Main Chain Parent Block</th>
|
||||
<th data-field="wideDifficulty">Wide Difficulty</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-block-template" role="tabpanel" aria-labelledby="pills-block-template-tab" tabindex="0">
|
||||
<div class="input-group flex-nowrap m-4">
|
||||
<span class="input-group-text" id="addon-wrapping">Address</span>
|
||||
<input type="text" class="form-control" aria-label="Address" aria-describedby="addon-wrapping">
|
||||
</div>
|
||||
|
||||
<div class="input-group flex-nowrap m-4">
|
||||
<span class="input-group-text" id="addon-wrapping">Reserve Size</span>
|
||||
<input type="number" class="form-control" aria-label="Reserve Size" aria-describedby="addon-wrapping">
|
||||
</div>
|
||||
|
||||
<div class="input-group m-4">
|
||||
<button class="btn btn-primary" type="button" id="getBlockTemplateButton">Get Block Template</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pills-disabled" role="tabpanel" aria-labelledby="pills-disabled-tab" tabindex="0">...</div>
|
||||
</div>
|
0
src/app/mining/mining.component.scss
Normal file
0
src/app/mining/mining.component.scss
Normal file
23
src/app/mining/mining.component.spec.ts
Normal file
23
src/app/mining/mining.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MiningComponent } from './mining.component';
|
||||
|
||||
describe('MiningComponent', () => {
|
||||
let component: MiningComponent;
|
||||
let fixture: ComponentFixture<MiningComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MiningComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MiningComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
132
src/app/mining/mining.component.ts
Normal file
132
src/app/mining/mining.component.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
import { AfterViewInit, Component } from '@angular/core';
|
||||
import { DaemonService } from '../core/services/daemon/daemon.service';
|
||||
import { NavbarService } from '../navbar/navbar.service';
|
||||
import { MinerData } from '../../common/MinerData';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { NavbarLink } from '../navbar/navbar.model';
|
||||
import { MineableTxBacklog } from '../../common/MineableTxBacklog';
|
||||
import { Chain } from '../../common/Chain';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mining',
|
||||
templateUrl: './mining.component.html',
|
||||
styleUrl: './mining.component.scss'
|
||||
})
|
||||
export class MiningComponent implements AfterViewInit {
|
||||
|
||||
private readonly navbarLinks: NavbarLink[];
|
||||
|
||||
private minerData?: MinerData;
|
||||
|
||||
private majorVersion: number;
|
||||
private height: number;
|
||||
private prevId: string;
|
||||
private seedHash: string;
|
||||
private difficulty: number;
|
||||
private medianWeight: number;
|
||||
private alreadyGeneratedCoins: number;
|
||||
private alternateChains: Chain[];
|
||||
//private txBacklog: MineableTxBacklog[]
|
||||
public cards: Card[];
|
||||
|
||||
constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService) {
|
||||
|
||||
this.majorVersion = 0;
|
||||
this.height = 0;
|
||||
this.prevId = '';
|
||||
this.seedHash = '';
|
||||
this.difficulty = 0;
|
||||
this.medianWeight = 0;
|
||||
this.alreadyGeneratedCoins = 0;
|
||||
this.alternateChains = [];
|
||||
this.cards = [];
|
||||
|
||||
this.navbarLinks = [
|
||||
new NavbarLink('pills-miner-data-tab', '#pills-miner-data', 'miner-data', true, 'Miner Data'),
|
||||
new NavbarLink('pills-alternate-chains-tab', '#pills-alternate-chains', 'alternate-chains', false, 'Alternate Chains'),
|
||||
new NavbarLink('pills-block-template-tab', '#pills-block-template', 'block-template', false, 'Block Template')
|
||||
];
|
||||
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
if (event.url != '/mining') return;
|
||||
this.onNavigationEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
console.log('DetailComponent AFTER VIEW INIT');
|
||||
|
||||
setTimeout(() => {
|
||||
const $table = $('#chainsTable');
|
||||
$table.bootstrapTable({});
|
||||
$table.bootstrapTable('refreshOptions', {
|
||||
classes: 'table table-bordered table-hover table-dark table-striped'
|
||||
});
|
||||
this.load();
|
||||
|
||||
}, 500);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
const $table = $('#chainsTable');
|
||||
$table.bootstrapTable('load', this.getChains());
|
||||
}
|
||||
|
||||
private createCards(): Card[] {
|
||||
return [
|
||||
new Card('Major Fork Version', `${this.majorVersion}`),
|
||||
new Card('Current block height', `${this.height}`),
|
||||
new Card('Previous Block Id', `${this.prevId}`),
|
||||
new Card('Seed hash', `${this.seedHash}`),
|
||||
new Card('Network difficulty', `${this.difficulty}`),
|
||||
new Card('Median block weight', `${this.medianWeight}`),
|
||||
new Card('Generated Coins', `${this.alreadyGeneratedCoins}`)
|
||||
];
|
||||
}
|
||||
|
||||
private getChains(): any[] {
|
||||
const chains: any[] = [];
|
||||
|
||||
this.alternateChains.forEach((chain: Chain) => chains.push({
|
||||
'blockHash': chain.blockHash,
|
||||
'height': chain.height,
|
||||
'length': chain.length,
|
||||
'mainChainParentBlock': chain.mainChainParentBlock,
|
||||
'wideDifficulty': chain.wideDifficulty
|
||||
}))
|
||||
|
||||
return chains;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Card {
|
||||
public header: string;
|
||||
public content: string;
|
||||
|
||||
constructor(header: string, content: string) {
|
||||
this.header = header;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
14
src/app/mining/mining.module.ts
Normal file
14
src/app/mining/mining.module.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { MiningRoutingModule } from './mining-routing.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MiningRoutingModule
|
||||
]
|
||||
})
|
||||
export class MiningModule { }
|
28
src/app/navbar/navbar.component.html
Normal file
28
src/app/navbar/navbar.component.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
<nav class="navbar navbar-expand-lg d-flex justify-content-center py-3 text-bg-dark">
|
||||
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
|
||||
<img src="/assets/icons/monero-symbol-on-white-480.png" width="40" height="40">
|
||||
<span class="fs-4">Monero Daemon</span>
|
||||
</a>
|
||||
|
||||
<ul class="nav nav-pills" id="pills-tab" role="tablist">
|
||||
@for(navbarLink of navbarLinks; track navbarLink.name) {
|
||||
<li class="nav-item" 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>
|
||||
}
|
||||
<!--
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="pills-home-tab" data-bs-toggle="pill" data-bs-target="#pills-home" type="button" role="tab" aria-controls="pills-home" aria-selected="true">Overview</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="pills-profile-tab" data-bs-toggle="pill" data-bs-target="#pills-profile" type="button" role="tab" aria-controls="pills-profile" aria-selected="false">Peers</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="pills-contact-tab" data-bs-toggle="pill" data-bs-target="#pills-contact" type="button" role="tab" aria-controls="pills-contact" aria-selected="false">Spans</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="pills-disabled-tab" data-bs-toggle="pill" data-bs-target="#pills-disabled" type="button" role="tab" aria-controls="pills-disabled" aria-selected="false" disabled>Disabled</button>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</nav>
|
0
src/app/navbar/navbar.component.scss
Normal file
0
src/app/navbar/navbar.component.scss
Normal file
23
src/app/navbar/navbar.component.spec.ts
Normal file
23
src/app/navbar/navbar.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarComponent } from './navbar.component';
|
||||
|
||||
describe('NavbarComponent', () => {
|
||||
let component: NavbarComponent;
|
||||
let fixture: ComponentFixture<NavbarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NavbarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(NavbarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
21
src/app/navbar/navbar.component.ts
Normal file
21
src/app/navbar/navbar.component.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { NavbarService } from './navbar.service';
|
||||
import { NavbarLink } from './navbar.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './navbar.component.html',
|
||||
styleUrl: './navbar.component.scss'
|
||||
})
|
||||
export class NavbarComponent {
|
||||
|
||||
public get navbarLinks(): NavbarLink[] {
|
||||
return this.navbarService.navbarLinks;
|
||||
}
|
||||
|
||||
constructor(private navbarService: NavbarService) {
|
||||
|
||||
}
|
||||
}
|
18
src/app/navbar/navbar.model.ts
Normal file
18
src/app/navbar/navbar.model.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
|
||||
|
||||
export class NavbarLink {
|
||||
public id: string;
|
||||
public target: string;
|
||||
public controls: string;
|
||||
public selected: boolean;
|
||||
public name: string;
|
||||
|
||||
constructor(id: string, target: string, controls: string, selected: boolean, name: string) {
|
||||
this.id = id;
|
||||
this.target = target;
|
||||
this.controls = controls;
|
||||
this.selected = selected;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
16
src/app/navbar/navbar.service.spec.ts
Normal file
16
src/app/navbar/navbar.service.spec.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarService } from './navbar.service';
|
||||
|
||||
describe('NavbarService', () => {
|
||||
let service: NavbarService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(NavbarService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
28
src/app/navbar/navbar.service.ts
Normal file
28
src/app/navbar/navbar.service.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { NavbarLink } from './navbar.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class NavbarService {
|
||||
private _navbarLinks: NavbarLink[] = [];
|
||||
|
||||
public get navbarLinks(): NavbarLink[] {
|
||||
return this._navbarLinks;
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
public addNavbarLink(... navbarLinks: NavbarLink[]): void {
|
||||
navbarLinks.forEach((navLink: NavbarLink) => this._navbarLinks.push(navLink));
|
||||
}
|
||||
|
||||
public setNavbarLinks(navbarLinks: NavbarLink[]): void {
|
||||
this._navbarLinks = navbarLinks;
|
||||
}
|
||||
|
||||
public removeNavbarLinks(): void {
|
||||
this.setNavbarLinks([]);
|
||||
}
|
||||
|
||||
}
|
12
src/app/sidebar/sidebar.component.html
Normal file
12
src/app/sidebar/sidebar.component.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="d-flex flex-column flex-shrink-0 p-3 text-bg-dark" style="width: 280px;">
|
||||
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
@for (navLink of navLinks; track navLink.title) {
|
||||
<a routerLink="{{navLink.path}}" [ngClass]="isActive(navLink) ? 'nav-link active' : 'nav-link text-white'">
|
||||
<i [class]="navLink.icon"></i>
|
||||
{{navLink.title}}
|
||||
</a>
|
||||
}
|
||||
</ul>
|
||||
<hr>
|
||||
</div>
|
0
src/app/sidebar/sidebar.component.scss
Normal file
0
src/app/sidebar/sidebar.component.scss
Normal file
23
src/app/sidebar/sidebar.component.spec.ts
Normal file
23
src/app/sidebar/sidebar.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SidebarComponent } from './sidebar.component';
|
||||
|
||||
describe('SidebarComponent', () => {
|
||||
let component: SidebarComponent;
|
||||
let fixture: ComponentFixture<SidebarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SidebarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SidebarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
44
src/app/sidebar/sidebar.component.ts
Normal file
44
src/app/sidebar/sidebar.component.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { CommonModule, NgClass, NgFor } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { ChildActivationEnd, ChildActivationStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterEvent, RouterModule, RoutesRecognized } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
standalone: true,
|
||||
templateUrl: './sidebar.component.html',
|
||||
styleUrl: './sidebar.component.scss',
|
||||
imports: [RouterModule, NgClass]
|
||||
})
|
||||
export class SidebarComponent {
|
||||
public readonly navLinks: NavLink[];
|
||||
public isLoading: boolean;
|
||||
public errorMessage: string;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.navLinks = [
|
||||
new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
|
||||
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'),
|
||||
];
|
||||
this.isLoading = false;
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
public isActive(navLink: NavLink): boolean {
|
||||
return navLink.path == this.router.url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NavLink {
|
||||
public readonly title: string;
|
||||
public readonly path: string;
|
||||
public readonly icon: string;
|
||||
|
||||
constructor(title: string, path: string, icon: string) {
|
||||
this.title = title;
|
||||
this.path = path;
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"GO_TO_DETAIL": "Go to Detail"
|
||||
},
|
||||
"DETAIL": {
|
||||
"TITLE": "Detail page !",
|
||||
"TITLE": "Monero Server Manager: Dashboard",
|
||||
"BACK_TO_HOME": "Back to Home"
|
||||
}
|
||||
}
|
||||
|
|
BIN
src/assets/icons/monero-symbol-on-white-480.png
Normal file
BIN
src/assets/icons/monero-symbol-on-white-480.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
30
src/common/Ban.ts
Normal file
30
src/common/Ban.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
export class Ban {
|
||||
public readonly host: string;
|
||||
public readonly ip: number;
|
||||
public readonly ban: boolean;
|
||||
public readonly seconds: number;
|
||||
|
||||
constructor(host: string, ip: number, ban: boolean, seconds: number) {
|
||||
this.host = host;
|
||||
this.ip = ip;
|
||||
this.ban = ban;
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
public toDictionary(): { [key: string]: any} {
|
||||
return {
|
||||
'host': this.host,
|
||||
'ip': this.ip,
|
||||
'ban': this.ban,
|
||||
'seconds': this.seconds
|
||||
}
|
||||
}
|
||||
|
||||
public static parse(ban: any): Ban {
|
||||
const host = ban.host;
|
||||
const ip = ban.ip;
|
||||
const seconds = ban.seconds;
|
||||
|
||||
return new Ban(host, ip, true, seconds);
|
||||
}
|
||||
}
|
29
src/common/BlockCount.ts
Normal file
29
src/common/BlockCount.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
export class BlockCount {
|
||||
public count: number;
|
||||
public status: string;
|
||||
public untrusted: boolean;
|
||||
|
||||
constructor(count: number, status: string, untrusted: boolean) {
|
||||
this.count = count;
|
||||
this.status = status;
|
||||
this.untrusted = untrusted;
|
||||
}
|
||||
|
||||
public static parse(blockCount: any): BlockCount {
|
||||
if (blockCount == null) {
|
||||
throw new Error("Cannot parse null value");
|
||||
}
|
||||
|
||||
const count: number = parseInt(blockCount.count);
|
||||
|
||||
if (typeof blockCount.status != "string") {
|
||||
throw new Error("Invalid block count status");
|
||||
}
|
||||
|
||||
const status: string = blockCount.status;
|
||||
const untrusted: boolean = blockCount.untrusted == true ? true : false;
|
||||
|
||||
return new BlockCount(count, status, untrusted);
|
||||
}
|
||||
}
|
106
src/common/BlockHeader.ts
Normal file
106
src/common/BlockHeader.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* {
|
||||
"block_size": 5500,
|
||||
"block_weight": 5500,
|
||||
"cumulative_difficulty": 86164894009456483,
|
||||
"cumulative_difficulty_top64": 0,
|
||||
"depth": 0,
|
||||
"difficulty": 227026389695,
|
||||
"difficulty_top64": 0,
|
||||
"hash": "a6ad87cf357a1aac1ee1d7cb0afa4c2e653b0b1ab7d5bf6af310333e43c59dd0",
|
||||
"height": 2286454,
|
||||
"long_term_weight": 5500,
|
||||
"major_version": 14,
|
||||
"miner_tx_hash": "a474f87de1645ff14c5e90c477b07f9bc86a22fb42909caa0705239298da96d0",
|
||||
"minor_version": 14,
|
||||
"nonce": 249602367,
|
||||
"num_txes": 3,
|
||||
"orphan_status": false,
|
||||
"pow_hash": "",
|
||||
"prev_hash": "fa17fefe1d05da775a61a3dc33d9e199d12af167ef0ab37e52b51e8487b50f25",
|
||||
"reward": 1181337498013,
|
||||
"timestamp": 1612088597,
|
||||
"wide_cumulative_difficulty": "0x1321e83bb8af763",
|
||||
"wide_difficulty": "0x34dbd3cabf"
|
||||
},
|
||||
*/
|
||||
|
||||
import { ThisReceiver } from "@angular/compiler";
|
||||
|
||||
export class BlockHeader {
|
||||
public readonly blockSize: number;
|
||||
public readonly blockWeight: number;
|
||||
public readonly cumulativeDifficulty: number;
|
||||
public readonly cumulativeDifficultyTop64: number;
|
||||
public readonly depth: number;
|
||||
public readonly difficulty: number;
|
||||
public readonly difficultyTop64: number;
|
||||
public readonly hash: string;
|
||||
public readonly height: number;
|
||||
public readonly longTermWeight: number;
|
||||
public readonly majorVersion: number;
|
||||
public readonly minerTxHash: string;
|
||||
public readonly minorVersion: number;
|
||||
public readonly nonce: number;
|
||||
public readonly numTxes: number;
|
||||
public readonly orphanStatus: boolean;
|
||||
public readonly powHash: string;
|
||||
public readonly prevHash: string;
|
||||
public readonly reward: number;
|
||||
public readonly timestamp: number;
|
||||
public readonly wideCumulativeDifficulty: string;
|
||||
public readonly wideDifficulty: string;
|
||||
|
||||
constructor(blockSize: number, blockWeight: number, cumulativeDifficulty: number, cumulativeDifficultyTop64: number, depth: number, difficulty: number, difficultyTop64: number, hash: string, height: number, longTermWeight: number, majorVersion: number
|
||||
, minerTxHash: string, minorVersion: number, nonce: number, numTxes: number, orphanStatus: boolean, powHash: string, prevHash: string, reward: number, timestamp: number, wideCumulativeDifficulty: string, wideDifficulty: string
|
||||
) {
|
||||
this.blockSize = blockSize;
|
||||
this.blockWeight = blockWeight;
|
||||
this.cumulativeDifficulty = cumulativeDifficulty;
|
||||
this.cumulativeDifficultyTop64 = cumulativeDifficultyTop64;
|
||||
this.depth = depth;
|
||||
this.difficulty = difficulty;
|
||||
this.difficultyTop64 = difficultyTop64;
|
||||
this.hash = hash;
|
||||
this.height = height;
|
||||
this.longTermWeight = longTermWeight;
|
||||
this.majorVersion = majorVersion;
|
||||
this.minerTxHash = minerTxHash;
|
||||
this.minorVersion = minorVersion;
|
||||
this.nonce = nonce;
|
||||
this.numTxes = numTxes;
|
||||
this.orphanStatus = orphanStatus;
|
||||
this.powHash = powHash;
|
||||
this.prevHash = prevHash;
|
||||
this.reward = reward;
|
||||
this.timestamp = timestamp;
|
||||
this.wideCumulativeDifficulty = wideCumulativeDifficulty;
|
||||
this.wideDifficulty = wideDifficulty;
|
||||
}
|
||||
|
||||
public static parse(blockHeader: any): BlockHeader {
|
||||
const blockSize = blockHeader.block_size;
|
||||
const blockWeight = blockHeader.block_weight;
|
||||
const cumulativeDifficulty = blockHeader.cumulative_difficulty;
|
||||
const cumulativeDifficultyTop64 = blockHeader.cumulative_difficulty_top64;
|
||||
const depth = blockHeader.depth;
|
||||
const difficulty = blockHeader.difficulty;
|
||||
const difficultyTop64 = blockHeader.difficulty_top64;
|
||||
const hash = blockHeader.hash;
|
||||
const height = blockHeader.height;
|
||||
const longTermWeight = blockHeader.long_term_weight;
|
||||
const majorVersion = blockHeader.major_version;
|
||||
const minerTxHash = blockHeader.miner_tx_hash;
|
||||
const minorVersion = blockHeader.minor_version;
|
||||
const nonce = blockHeader.nonce;
|
||||
const numTxes = blockHeader.num_txes;
|
||||
const orphanStatus = blockHeader.orphan_status;
|
||||
const powHash = blockHeader.pow_hash;
|
||||
const prevHash = blockHeader.prev_hash;
|
||||
const reward = blockHeader.reward;
|
||||
const timestamp = blockHeader.timestamp;
|
||||
const wideCumulativeDifficulty = blockHeader.wide_cumulative_difficulty;
|
||||
const wideDifficulty = blockHeader.wide_difficulty;
|
||||
return new BlockHeader(blockSize, blockWeight, cumulativeDifficulty, cumulativeDifficultyTop64, depth, difficulty, difficultyTop64, hash, height, longTermWeight, majorVersion, minerTxHash, minorVersion, nonce, numTxes, orphanStatus, powHash, prevHash, reward, timestamp, wideCumulativeDifficulty, wideDifficulty);
|
||||
}
|
||||
}
|
51
src/common/BlockTemplate.ts
Normal file
51
src/common/BlockTemplate.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
export class BlockTemplate {
|
||||
public readonly blockHashingBlob: string;
|
||||
public readonly blockTemplateBlob: string;
|
||||
public readonly difficulty: number;
|
||||
public readonly difficultyTop64: number;
|
||||
public readonly expectedReward: number;
|
||||
public readonly height: number;
|
||||
public readonly nextSeedHash: string;
|
||||
public readonly prevHash: string;
|
||||
public readonly reservedOffset: number;
|
||||
public readonly seedHash: string;
|
||||
public readonly seedHeight: number;
|
||||
public readonly status: string;
|
||||
public readonly untrusted: boolean;
|
||||
public readonly wideDifficulty: string;
|
||||
|
||||
constructor(blockHashingBlob: string, blockTemplateBlob: string, difficulty: number, difficultyTop64: number, expectedReward: number, height: number, nextSeedHash: string, prevHash: string, reservedOffset: number, seedHash: string, seedHeight: number, status: string, untrusted: boolean, wideDifficulty: string) {
|
||||
this.blockHashingBlob = blockHashingBlob;
|
||||
this.blockTemplateBlob = blockTemplateBlob;
|
||||
this.difficulty = difficulty;
|
||||
this.difficultyTop64 = difficultyTop64;
|
||||
this.expectedReward = expectedReward;
|
||||
this.height = height;
|
||||
this.nextSeedHash = nextSeedHash;
|
||||
this.prevHash = prevHash;
|
||||
this.reservedOffset = reservedOffset;
|
||||
this.seedHash = seedHash;
|
||||
this.seedHeight = seedHeight;
|
||||
this.status = status;
|
||||
this.untrusted = untrusted;
|
||||
this.wideDifficulty = wideDifficulty;
|
||||
}
|
||||
|
||||
public static parse(blockTemplate: any): BlockTemplate {
|
||||
const blockHashingBlob = blockTemplate.blockhashing_blob;
|
||||
const blockTemplateBlob = blockTemplate.blocktemplate_blob;
|
||||
const difficulty = blockTemplate.difficulty;
|
||||
const difficultyTop64 = blockTemplate.difficulty_top64;
|
||||
const expectedReward = blockTemplate.expected_reward;
|
||||
const height = blockTemplate.height;
|
||||
const nextSeedHash = blockTemplate.next_seed_hash;
|
||||
const prevHash = blockTemplate.prev_hash;
|
||||
const reservedOffset = blockTemplate.reserved_offset;
|
||||
const seedHash = blockTemplate.seed_hash;
|
||||
const seedHeight = blockTemplate.seed_height;
|
||||
const status = blockTemplate.status;
|
||||
const untrusted = blockTemplate.untrusted;
|
||||
const wideDifficulty = blockTemplate.wide_difficulty;
|
||||
return new BlockTemplate(blockHashingBlob, blockTemplateBlob, difficulty, difficultyTop64, expectedReward, height, nextSeedHash, prevHash, reservedOffset, seedHash, seedHeight, status, untrusted, wideDifficulty);
|
||||
}
|
||||
}
|
16
src/common/BlockchainPruneInfo.ts
Normal file
16
src/common/BlockchainPruneInfo.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export class BlockchainPruneInfo {
|
||||
public readonly pruned: boolean;
|
||||
public readonly pruningSeed: number;
|
||||
|
||||
constructor(pruned: boolean, pruningSeed: number) {
|
||||
this.pruned = pruned;
|
||||
this.pruningSeed = pruningSeed;
|
||||
}
|
||||
|
||||
public static parse(info: any): BlockchainPruneInfo {
|
||||
const pruned = info.pruned;
|
||||
const pruningSeed = info.pruning_seed;
|
||||
|
||||
return new BlockchainPruneInfo(pruned, pruningSeed);
|
||||
}
|
||||
}
|
34
src/common/Chain.ts
Normal file
34
src/common/Chain.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
export class Chain {
|
||||
public readonly blockHash: string;
|
||||
public readonly blockHashes: string[];
|
||||
public readonly difficulty: number;
|
||||
public readonly difficultyTop64: number;
|
||||
public readonly height: number;
|
||||
public readonly length: number;
|
||||
public readonly mainChainParentBlock: string;
|
||||
public readonly wideDifficulty: string;
|
||||
|
||||
constructor(blockHash: string, blockHashes: string[], difficulty: number, difficultyTop64: number, height: number, length: number, mainChainParentBlock: string, wideDifficulty: string) {
|
||||
this.blockHash = blockHash;
|
||||
this.blockHashes = blockHashes;
|
||||
this.difficulty = difficulty;
|
||||
this.difficultyTop64 = difficultyTop64;
|
||||
this.height = height;
|
||||
this.length = length;
|
||||
this.mainChainParentBlock = mainChainParentBlock;
|
||||
this.wideDifficulty = wideDifficulty;
|
||||
}
|
||||
|
||||
public static parse(chain: any): Chain {
|
||||
const blockHash = chain.block_hash;
|
||||
const blockHashes = chain.block_hashes;
|
||||
const difficulty = chain.difficulty;
|
||||
const difficultyTop64 = chain.difficulty_top64;
|
||||
const height = chain.height;
|
||||
const length = chain.length;
|
||||
const mainChainParentBlock = chain.main_chain_parent_block;
|
||||
const wideDifficulty = chain.wide_difficulty;
|
||||
|
||||
return new Chain(blockHash, blockHashes, difficulty, difficultyTop64, height, length, mainChainParentBlock, wideDifficulty);
|
||||
}
|
||||
}
|
102
src/common/Connection.ts
Normal file
102
src/common/Connection.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
*
|
||||
address - string; The peer's address, actually IPv4 & port
|
||||
avg_download - unsigned int; Average bytes of data downloaded by node.
|
||||
avg_upload - unsigned int; Average bytes of data uploaded by node.
|
||||
connection_id - string; The connection ID
|
||||
current_download - unsigned int; Current bytes downloaded by node.
|
||||
current_upload - unsigned int; Current bytes uploaded by node.
|
||||
height- unsigned int; The peer height
|
||||
host - string; The peer host
|
||||
incoming - boolean; Is the node getting information from your node?
|
||||
ip - string; The node's IP address.
|
||||
live_time - unsigned int
|
||||
local_ip - boolean
|
||||
localhost - boolean
|
||||
peer_id - string; The node's ID on the network.
|
||||
port - string; The port that the node is using to connect to the network.
|
||||
recv_count - unsigned int
|
||||
recv_idle_time - unsigned int
|
||||
send_count - unsigned int
|
||||
send_idle_time - unsigned int
|
||||
state - string
|
||||
support_flags - unsigned int
|
||||
|
||||
*/
|
||||
|
||||
export class Connection {
|
||||
public readonly address: string;
|
||||
public readonly avgDownload: number;
|
||||
public readonly avgUpload: number;
|
||||
public readonly connectionId: string;
|
||||
public readonly currentDownload: number;
|
||||
public readonly currentUpload: number;
|
||||
public readonly height: number;
|
||||
public readonly host: number;
|
||||
public readonly incoming: boolean;
|
||||
public readonly ip: string;
|
||||
public readonly liveTime: number;
|
||||
public readonly localIp: boolean;
|
||||
public readonly localhost: boolean;
|
||||
public readonly peerId: string;
|
||||
public readonly port: string;
|
||||
public readonly pruningSeed: number;
|
||||
public readonly recvCount: number;
|
||||
public readonly recvIdleTime: number;
|
||||
public readonly sendCount: number;
|
||||
public readonly sendIdleTime: number;
|
||||
public readonly state: string;
|
||||
public readonly supportFlags: number;
|
||||
|
||||
constructor(address: string, avgDownload: number, avgUpload: number, connectionId: string, currentDownload: number, currentUpload: number, height: number, host: number, incoming: boolean, ip: string, liveTime: number, localIp: boolean, localhost: boolean, peerId: string, port: string, pruningSeed: number, recvCount: number, recvIdleTime: number, sendCount: number, sendIdleTime: number, state: string, supportFlags: number) {
|
||||
this.address = address;
|
||||
this.avgDownload = avgDownload;
|
||||
this.avgUpload = avgUpload;
|
||||
this.connectionId = connectionId;
|
||||
this.currentDownload = currentDownload;
|
||||
this.currentUpload = currentUpload;
|
||||
this.height = height;
|
||||
this.host = host;
|
||||
this.incoming = incoming;
|
||||
this.ip = ip;
|
||||
this.liveTime = liveTime;
|
||||
this.localIp = localIp;
|
||||
this.localhost = localhost;
|
||||
this.peerId = peerId;
|
||||
this.port = port;
|
||||
this.pruningSeed = pruningSeed
|
||||
this.recvCount = recvCount;
|
||||
this.recvIdleTime = recvIdleTime;
|
||||
this.sendCount = sendCount;
|
||||
this.sendIdleTime = sendIdleTime;
|
||||
this.state = state;
|
||||
this.supportFlags = supportFlags;
|
||||
}
|
||||
|
||||
public static parse(connection: any): Connection {
|
||||
const address = connection.address;
|
||||
const avgDownload = connection.avg_download;
|
||||
const avgUpload = connection.avg_upload;
|
||||
const connectionId = connection.connection_id;
|
||||
const currentDownload = connection.current_download;
|
||||
const currentUpload = connection.current_upload;
|
||||
const height = connection.height;
|
||||
const host = connection.host;
|
||||
const incoming = connection.incoming;
|
||||
const ip = connection.ip;
|
||||
const liveTime = connection.live_time;
|
||||
const localIp = connection.local_ip;
|
||||
const localhost = connection.localhost;
|
||||
const peerId = connection.peer_id;
|
||||
const port = connection.port;
|
||||
const pruningSeed = connection.pruning_seed;
|
||||
const recvCount = connection.recv_count;
|
||||
const recvIdleTime = connection.recv_idle_time;
|
||||
const sendCount = connection.send_count;
|
||||
const sendIdleTime = connection.send_idle_time;
|
||||
const state = connection.state;
|
||||
const supportFlags = connection.support_flags;
|
||||
|
||||
return new Connection(address, avgDownload, avgUpload, connectionId, currentDownload, currentUpload, height, host, incoming, ip, liveTime, localIp, localhost, peerId, port, pruningSeed, recvCount, recvIdleTime, sendCount, sendIdleTime, state, supportFlags);
|
||||
}
|
||||
}
|
184
src/common/DaemonInfo.ts
Normal file
184
src/common/DaemonInfo.ts
Normal file
|
@ -0,0 +1,184 @@
|
|||
export class DaemonInfo {
|
||||
|
||||
public readonly adjustedTime: number;
|
||||
public readonly altBlocksCount: number;
|
||||
public readonly blockSizeLimit: number;
|
||||
public readonly blockSizeMedian: number;
|
||||
public readonly bootstrapDaemonAddress: string;
|
||||
public readonly busySyncing: boolean;
|
||||
public readonly credits: number;
|
||||
public readonly cumulativeDifficulty: number;
|
||||
public readonly cumulativeDifficultyTop64: number;
|
||||
public readonly databaseSize: number;
|
||||
public readonly difficulty: number;
|
||||
public readonly difficultyTop64: number;
|
||||
public readonly freeSpace: number;
|
||||
public readonly greyPeerlistSize: number;
|
||||
public readonly height: number;
|
||||
public readonly heightWithoutBootstrap: number;
|
||||
public readonly incomingConnectionsCount: number;
|
||||
public readonly mainnet: boolean;
|
||||
public readonly nettype: string;
|
||||
public readonly offline: boolean;
|
||||
public readonly outgoingConnectionsCount: number;
|
||||
public readonly rpcConnectionsCount: number;
|
||||
public readonly stagenet: boolean;
|
||||
public readonly startTime: number;
|
||||
public readonly status: string;
|
||||
public readonly synchronized: boolean;
|
||||
public readonly target: number;
|
||||
public readonly targetHeight: number;
|
||||
public readonly testnet: boolean;
|
||||
public readonly topBlockHash: string;
|
||||
public readonly topHash: string;
|
||||
public readonly txCount: number;
|
||||
public readonly txPoolSize: number;
|
||||
public readonly untrusted: boolean;
|
||||
public readonly updateAvailable: boolean;
|
||||
public readonly version: string;
|
||||
public readonly wasBoostrapEverUsed: boolean;
|
||||
public readonly whitePeerlistSize: number;
|
||||
public readonly wideCumulativeDifficulty: string;
|
||||
public readonly wideDifficulty: string;
|
||||
|
||||
constructor(
|
||||
adjustedTime: number,
|
||||
altBlocksCount: number,
|
||||
blockSizeLimit: number,
|
||||
blockSizeMedian: number,
|
||||
bootstrapDaemonAddress: string,
|
||||
busySyncing: boolean,
|
||||
credits: number,
|
||||
cumulativeDifficulty: number,
|
||||
cumulativeDifficultyTop64: number,
|
||||
databaseSize: number,
|
||||
difficulty: number,
|
||||
difficultyTop64: number,
|
||||
freeSpace: number,
|
||||
greyPeerlistSize: number,
|
||||
height: number,
|
||||
heightWithoutBootstrap: number,
|
||||
incomingConnectionsCount: number,
|
||||
mainnet: boolean,
|
||||
nettype: string,
|
||||
offline: boolean,
|
||||
outgoingConnectionsCount: number,
|
||||
rpcConnectionsCount: number,
|
||||
stagenet: boolean,
|
||||
startTime: number,
|
||||
status: string,
|
||||
synchronized: boolean,
|
||||
target: number,
|
||||
targetHeight: number,
|
||||
testnet: boolean,
|
||||
topBlockHash: string,
|
||||
topHash: string,
|
||||
txCount: number,
|
||||
txPoolSize: number,
|
||||
untrusted: boolean,
|
||||
updateAvailable: boolean,
|
||||
version: string,
|
||||
wasBoostrapEverUsed: boolean,
|
||||
whitePeerlistSize: number,
|
||||
wideCumulativeDifficulty: string,
|
||||
wideDifficulty: string
|
||||
) {
|
||||
this.adjustedTime = adjustedTime;
|
||||
this.altBlocksCount = altBlocksCount;
|
||||
this.blockSizeLimit = blockSizeLimit;
|
||||
this.blockSizeMedian = blockSizeMedian;
|
||||
this.bootstrapDaemonAddress = bootstrapDaemonAddress;
|
||||
this.busySyncing = busySyncing;
|
||||
this.credits = credits;
|
||||
this.cumulativeDifficulty = cumulativeDifficulty;
|
||||
this.cumulativeDifficultyTop64 = cumulativeDifficultyTop64;
|
||||
this.databaseSize = databaseSize;
|
||||
this.difficulty = difficulty;
|
||||
this.difficultyTop64 = difficultyTop64;
|
||||
this.freeSpace = freeSpace;
|
||||
this.greyPeerlistSize = greyPeerlistSize;
|
||||
this.height = height;
|
||||
this.heightWithoutBootstrap = heightWithoutBootstrap;
|
||||
this.incomingConnectionsCount = incomingConnectionsCount;
|
||||
this.mainnet = mainnet;
|
||||
this.nettype = nettype;
|
||||
this.offline = offline;
|
||||
this.outgoingConnectionsCount = outgoingConnectionsCount;
|
||||
this.rpcConnectionsCount = rpcConnectionsCount;
|
||||
this.stagenet = stagenet;
|
||||
this.startTime = startTime;
|
||||
this.status = status;
|
||||
this.synchronized = synchronized;
|
||||
this.target = target;
|
||||
this.targetHeight = targetHeight;
|
||||
this.testnet = testnet;
|
||||
this.topBlockHash = topBlockHash;
|
||||
this.topHash = topHash;
|
||||
this.txCount = txCount;
|
||||
this.txPoolSize = txPoolSize;
|
||||
this.untrusted = untrusted;
|
||||
this.updateAvailable = updateAvailable;
|
||||
this.version = version;
|
||||
this.wasBoostrapEverUsed = wasBoostrapEverUsed;
|
||||
this.whitePeerlistSize = whitePeerlistSize;
|
||||
this.wideCumulativeDifficulty = wideCumulativeDifficulty;
|
||||
this.wideDifficulty = wideDifficulty;
|
||||
}
|
||||
|
||||
public static parse(info: any): DaemonInfo {
|
||||
const adjustedTime = info.adjusted_time;
|
||||
const altBlocksCount = info.alt_blocks_count;
|
||||
const blockSizeLimit = info.block_size_limit;
|
||||
const blockSizeMedian = info.block_size_median;
|
||||
const bootstrapDaemonAddress = info.bootstrap_daemon_address;
|
||||
const busySyncing = info.busy_syncing;
|
||||
const credits = info.credits;
|
||||
const cumulativeDifficulty = info.cumulative_difficulty;
|
||||
const cumulativeDifficultyTop64 = info.cumulative_difficulty_top64;
|
||||
const databaseSize = info.database_size;
|
||||
const difficulty = info.difficulty;
|
||||
const difficultyTop64 = info.difficulty_top64;
|
||||
const freeSpace = info.free_space;
|
||||
const greyPeerlistSize = info.grey_peerlist_size;
|
||||
const height = info.height;
|
||||
const heightWithoutBootstrap = info.height_without_bootstrap;
|
||||
const incomingConnectionsCount = info.incoming_connections_count;
|
||||
const mainnet = info.mainnet;
|
||||
const nettype = info.nettype;
|
||||
const offline = info.offline;
|
||||
const outgoingConnectionsCount = info.outgoing_connections_count;
|
||||
const rpcConnectionsCount = info.rpc_connections_count;
|
||||
const stagenet = info.stagenet;
|
||||
const startTime = info.start_time;
|
||||
const status = info.status;
|
||||
const synchronized = info.synchronized;
|
||||
const target = info.target;
|
||||
const targetHeight = info.target_height;
|
||||
const testnet = info.testnet;
|
||||
const topBlockHash = info.top_block_hash;
|
||||
const topHash = info.top_hash;
|
||||
const txCount = info.tx_count;
|
||||
const txPoolSize = info.tx_pool_size;
|
||||
const untrusted = info.untrusted;
|
||||
const updateAvailable = info.update_available;
|
||||
const version = info.version;
|
||||
const wasBoostrapEverUsed = info.was_boostrap_ever_used;
|
||||
const whitePeerlistSize = info.white_peerlist_size;
|
||||
const wideCumulativeDifficulty = info.wide_cumulative_difficulty;
|
||||
const wideDifficulty = info.wide_difficulty;
|
||||
|
||||
|
||||
return new DaemonInfo(
|
||||
adjustedTime, altBlocksCount, blockSizeLimit, blockSizeMedian,
|
||||
bootstrapDaemonAddress, busySyncing, credits, cumulativeDifficulty,
|
||||
cumulativeDifficultyTop64, databaseSize, difficulty,
|
||||
difficultyTop64, freeSpace, greyPeerlistSize,
|
||||
height, heightWithoutBootstrap, incomingConnectionsCount, mainnet,
|
||||
nettype, offline, outgoingConnectionsCount, rpcConnectionsCount,
|
||||
stagenet, startTime, status, synchronized, target, targetHeight,
|
||||
testnet, topBlockHash, topHash, txCount, txPoolSize, untrusted,
|
||||
updateAvailable, version, wasBoostrapEverUsed, whitePeerlistSize,
|
||||
wideCumulativeDifficulty, wideDifficulty
|
||||
);
|
||||
}
|
||||
}
|
16
src/common/DaemonVersion.ts
Normal file
16
src/common/DaemonVersion.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export class DaemonVersion {
|
||||
public readonly version: number;
|
||||
public readonly release: boolean;
|
||||
|
||||
constructor(version: number, release: boolean) {
|
||||
this.version = version;
|
||||
this.release = release;
|
||||
}
|
||||
|
||||
public static parse(version: any) {
|
||||
const v: number = version.version;
|
||||
const release: boolean = version.release;
|
||||
|
||||
return new DaemonVersion(v, release);
|
||||
}
|
||||
}
|
19
src/common/FeeEstimate.ts
Normal file
19
src/common/FeeEstimate.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export class FeeEstimate {
|
||||
public readonly fee: number;
|
||||
public readonly fees: number[];
|
||||
public readonly quantizationMask: number;
|
||||
|
||||
constructor(fee: number, fees: number[], quantizationMask: number) {
|
||||
this.fee = fee;
|
||||
this.fees = fees;
|
||||
this.quantizationMask = quantizationMask;
|
||||
}
|
||||
|
||||
public static parse(estimate: any): FeeEstimate {
|
||||
const fee = estimate.fee;
|
||||
const fees = estimate.fees;
|
||||
const quantizationMask = estimate.quantization_mask;
|
||||
|
||||
return new FeeEstimate(fee, fees, quantizationMask);
|
||||
}
|
||||
}
|
22
src/common/GeneratedBlocks.ts
Normal file
22
src/common/GeneratedBlocks.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
export class GeneratedBlocks {
|
||||
public readonly blocks: string[];
|
||||
public readonly height: number;
|
||||
public readonly status: string;
|
||||
public readonly untrusted: boolean;
|
||||
|
||||
constructor(blocks: string[], height: number, status: string, untrusted: boolean) {
|
||||
this.blocks = blocks;
|
||||
this.height = height;
|
||||
this.status = status;
|
||||
this.untrusted = untrusted;
|
||||
}
|
||||
|
||||
public static parse(generatedBlocks: any): GeneratedBlocks {
|
||||
const blocks: string[] = generatedBlocks.blocks;
|
||||
const height: number = generatedBlocks.height;
|
||||
const status: string = generatedBlocks.status;
|
||||
const untrusted: boolean = generatedBlocks.untrusted;
|
||||
|
||||
return new GeneratedBlocks(blocks, height, status, untrusted);
|
||||
}
|
||||
}
|
37
src/common/HardForkInfo.ts
Normal file
37
src/common/HardForkInfo.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
export class HardForkInfo {
|
||||
public readonly earliestHeight: number;
|
||||
public readonly enabled: boolean;
|
||||
public readonly state: number;
|
||||
public readonly threshold: number;
|
||||
public readonly topHash: string;
|
||||
public readonly version: number;
|
||||
public readonly votes: number;
|
||||
public readonly voting: number;
|
||||
public readonly window: number;
|
||||
|
||||
constructor(earliestHeight: number, enabled: boolean, state: number, threshold: number, topHash: string, version: number, votes: number, voting: number, window: number) {
|
||||
this.earliestHeight = earliestHeight;
|
||||
this.enabled = enabled;
|
||||
this.state = state;
|
||||
this.threshold = threshold;
|
||||
this.topHash = topHash;
|
||||
this.version = version;
|
||||
this.votes = votes;
|
||||
this.voting = voting;
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
public static parse(hardForkInfo: any): HardForkInfo {
|
||||
const earliestHeight = hardForkInfo.earliest_height;
|
||||
const enabled = hardForkInfo.enabled;
|
||||
const state = hardForkInfo.state;
|
||||
const threshold = hardForkInfo.threshold;
|
||||
const topHash = hardForkInfo.top_hash;
|
||||
const version = hardForkInfo.version;
|
||||
const votes = hardForkInfo.votes;
|
||||
const voting = hardForkInfo.voting;
|
||||
const window = hardForkInfo.window;
|
||||
|
||||
return new HardForkInfo(earliestHeight, enabled, state, threshold, topHash, version, votes, voting, window);
|
||||
}
|
||||
}
|
22
src/common/HistogramEntry.ts
Normal file
22
src/common/HistogramEntry.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
export class HistogramEntry {
|
||||
public readonly amount: number;
|
||||
public readonly totalInstances: number;
|
||||
public readonly unlockedInstances: number;
|
||||
public readonly recentInstances: number;
|
||||
|
||||
constructor(amount: number, totalInstances: number, unlockedInstances: number, recentInstances: number) {
|
||||
this.amount = amount;
|
||||
this.totalInstances = totalInstances;
|
||||
this.unlockedInstances = unlockedInstances;
|
||||
this.recentInstances = recentInstances;
|
||||
}
|
||||
|
||||
public static parse(entry: any) {
|
||||
const amount = entry.amount;
|
||||
const totalInstances = entry.total_instances;
|
||||
const unlockedInstances = entry.unlocked_instances;
|
||||
const recentInstances = entry.recent_instances;
|
||||
|
||||
return new HistogramEntry(amount, totalInstances, unlockedInstances, recentInstances);
|
||||
}
|
||||
}
|
19
src/common/MineableTxBacklog.ts
Normal file
19
src/common/MineableTxBacklog.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export class MineableTxBacklog {
|
||||
public readonly id: string;
|
||||
public readonly weight: number;
|
||||
public readonly fee: number;
|
||||
|
||||
constructor(id: string, weight: number, fee: number) {
|
||||
this.id = id;
|
||||
this.weight = weight;
|
||||
this.fee = fee;
|
||||
}
|
||||
|
||||
public static parse(txBacklog: any): MineableTxBacklog {
|
||||
const id: string = txBacklog.id;
|
||||
const weight: number = txBacklog.weight;
|
||||
const fee: number = txBacklog.fee;
|
||||
|
||||
return new MineableTxBacklog(id, weight, fee);
|
||||
}
|
||||
}
|
36
src/common/MinerData.ts
Normal file
36
src/common/MinerData.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { MineableTxBacklog } from "./MineableTxBacklog";
|
||||
|
||||
export class MinerData {
|
||||
public readonly majorVersion: number;
|
||||
public readonly height: number;
|
||||
public readonly prevId: string;
|
||||
public readonly seedHash: string;
|
||||
public readonly difficulty: number;
|
||||
public readonly medianWeight: number;
|
||||
public readonly alreadyGeneratedCoins: number;
|
||||
public readonly txBacklog: MineableTxBacklog[];
|
||||
|
||||
constructor(majorVersion: number, height: number, prevId: string, seedHash: string, difficulty: number, medianWeight: number, alreadyGeneratedCoins: number, txBacklog: MineableTxBacklog[]) {
|
||||
this.majorVersion = majorVersion;
|
||||
this.height = height;
|
||||
this.prevId = prevId;
|
||||
this.seedHash = seedHash;
|
||||
this.difficulty = difficulty;
|
||||
this.medianWeight = medianWeight;
|
||||
this.alreadyGeneratedCoins = alreadyGeneratedCoins;
|
||||
this.txBacklog = txBacklog;
|
||||
}
|
||||
|
||||
public static parse(minerData: any): MinerData {
|
||||
const majorVersion = minerData.major_version;
|
||||
const height = minerData.height;
|
||||
const prevId = minerData.prev_id;
|
||||
const seedHash = minerData.seed_hash;
|
||||
const difficulty = minerData.difficulty;
|
||||
const medianWeight = minerData.median_weight;
|
||||
const alreadyGeneratedCoins = minerData.already_generated_coins;
|
||||
const txBacklog = minerData.tx_backlog;
|
||||
|
||||
return new MinerData(majorVersion, height, prevId, seedHash, difficulty, medianWeight, alreadyGeneratedCoins, txBacklog);
|
||||
}
|
||||
}
|
14
src/common/Peer.ts
Normal file
14
src/common/Peer.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Connection } from "./Connection";
|
||||
|
||||
export class Peer {
|
||||
public readonly info: Connection;
|
||||
|
||||
constructor(info: Connection) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public static parse(peer: any): Peer {
|
||||
const info: any = peer.info;
|
||||
return new Peer(Connection.parse(info));
|
||||
}
|
||||
}
|
43
src/common/Span.ts
Normal file
43
src/common/Span.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
*
|
||||
connection_id - string; Id of connection
|
||||
nblocks - unsigned int; number of blocks in that span
|
||||
rate - unsigned int; connection rate
|
||||
remote_address - string; peer address the node is downloading (or has downloaded) than span from
|
||||
size - unsigned int; total number of bytes in that span's blocks (including txes)
|
||||
speed - unsigned int; connection speed
|
||||
start_block_height - unsigned int; block height of the first block in that span
|
||||
|
||||
*/
|
||||
|
||||
export class Span {
|
||||
public readonly connectionId: string;
|
||||
public readonly nBlocks: number;
|
||||
public readonly rate: number;
|
||||
public readonly remoteAddress: string;
|
||||
public readonly size: number;
|
||||
public readonly speed: number;
|
||||
public readonly startBlockHeight: number;
|
||||
|
||||
constructor(connectionId: string, nBlocks: number, rate: number, remoteAddress: string, size: number, speed: number, startBlockHeight: number) {
|
||||
this.connectionId = connectionId;
|
||||
this.nBlocks = nBlocks;
|
||||
this.rate = rate;
|
||||
this.remoteAddress = remoteAddress;
|
||||
this.size = size;
|
||||
this.speed = speed;
|
||||
this.startBlockHeight = startBlockHeight;
|
||||
}
|
||||
|
||||
public static parse(span: any): Span {
|
||||
const connectionId: string = span.connection_id;
|
||||
const nBlocks: number = span.nblocks;
|
||||
const rate: number = span.rate;
|
||||
const remoteAddress: string = span.remote_address;
|
||||
const size: number = span.size;
|
||||
const speed: number = span.speed;
|
||||
const startBlockHeight = span.start_block_height;
|
||||
|
||||
return new Span(connectionId, nBlocks, rate, remoteAddress, size, speed, startBlockHeight);
|
||||
}
|
||||
}
|
37
src/common/SyncInfo.ts
Normal file
37
src/common/SyncInfo.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Peer } from "./Peer";
|
||||
import { Span } from "./Span";
|
||||
|
||||
export class SyncInfo {
|
||||
public readonly height: number;
|
||||
public readonly targetHeight: number;
|
||||
public readonly nextNeededPruningSeed: number;
|
||||
public readonly overview: string;
|
||||
public readonly peers: Peer[];
|
||||
public readonly spans: Span[];
|
||||
|
||||
constructor(height: number, targetHeight: number, nextNeededPruningSeed: number, overview: string, peers: Peer[], spans: Span[]) {
|
||||
this.height = height;
|
||||
this.targetHeight = targetHeight;
|
||||
this.nextNeededPruningSeed = nextNeededPruningSeed;
|
||||
this.overview = overview;
|
||||
this.peers = peers;
|
||||
this.spans = spans;
|
||||
}
|
||||
|
||||
public static parse(syncInfo: any): SyncInfo {
|
||||
const height = syncInfo.height;
|
||||
const targetHeight = syncInfo.target_height;
|
||||
const nextNeededPruningSeed = syncInfo.next_needed_pruning_seed;
|
||||
const overview = syncInfo.overview;
|
||||
const peers: Peer[] = [];
|
||||
const spans: Span[] = [];
|
||||
const rawPeers: any[] = syncInfo.peers;
|
||||
const rawSpans: any[] | undefined = syncInfo.rawSpans;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
15
src/common/TxBacklogEntry.ts
Normal file
15
src/common/TxBacklogEntry.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export class TxBacklogEntry {
|
||||
public readonly blobSize: number;
|
||||
public readonly fee: number;
|
||||
public readonly timeInPool: number;
|
||||
|
||||
constructor(blobSize: number, fee: number, timeInPool: number) {
|
||||
this.blobSize = blobSize;
|
||||
this.fee = fee;
|
||||
this.timeInPool = timeInPool;
|
||||
}
|
||||
|
||||
public static fromBinary(binary: string): TxBacklogEntry[] {
|
||||
throw new Error("TxBacklogEntry.fromBinary(): not implemented");
|
||||
}
|
||||
}
|
28
src/common/request.ts
Normal file
28
src/common/request.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
export { JsonRPCRequest } from "./request/JsonRPCRequest";
|
||||
export { GetBlockCountRequest } from "./request/GetBlockCountRequest";
|
||||
export { GetBlockHashRequest } from "./request/GetBlockHashRequest";
|
||||
export { GetBlockTemplateRequest } from "./request/GetBlockTemplateRequest";
|
||||
export { SubmitBlockRequest } from "./request/SubmitBlockRequest";
|
||||
export { GenerateBlocksRequest } from "./request/GenerateBlocksRequest";
|
||||
export { GetLastBlockHeaderRequest } from "./request/GetLastBlockHeaderRequest";
|
||||
export { GetBlockHeaderByHashRequest } from "./request/GetBlockHeaderByHashRequest";
|
||||
export { GetBlockHeaderByHeightRequest } from "./request/GetBlockHeaderByHeightRequest";
|
||||
export { GetBlockHeadersRangeRequest } from "./request/GetBlockHeadersRangeRequest";
|
||||
export { GetConnectionsRequest } from "./request/GetConnectionsRequest";
|
||||
export { GetInfoRequest } from "./request/GetInfoRequest";
|
||||
export { HardForkInfoRequest } from "./request/HardForkInfoRequest";
|
||||
export { SetBansRequest } from "./request/SetBansRequest";
|
||||
export { GetBansRequest } from "./request/GetBansRequest";
|
||||
export { BannedRequest } from "./request/BannedRequest";
|
||||
export { FlushTxPoolRequest } from "./request/FlushTxPoolRequest";
|
||||
export { GetOutputHistogramRequest } from "./request/GetOutputHistogramRequest";
|
||||
export { SyncInfoRequest } from "./request/SyncInfoRequest";
|
||||
export { GetVersionRequest } from "./request/GetVersionRequest";
|
||||
export { GetFeeEstimateRequest } from "./request/GetFeeEstimateRequest";
|
||||
export { GetAlternateChainsRequest } from "./request/GetAlternateChainsRequest";
|
||||
export { RelayTxRequest } from "./request/RelayTxRequest";
|
||||
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";
|
21
src/common/request/BannedRequest.ts
Normal file
21
src/common/request/BannedRequest.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class BannedRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'banned';
|
||||
public readonly address: string;
|
||||
|
||||
constructor(address: string) {
|
||||
super();
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'address': this.address
|
||||
};
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
31
src/common/request/CalculatePoWHashRequest.ts
Normal file
31
src/common/request/CalculatePoWHashRequest.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class CalculatePoWHashRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'calc_pow';
|
||||
public readonly majorVersion: number;
|
||||
public readonly height: number;
|
||||
public readonly blockBlob: string;
|
||||
public readonly seedHash: string;
|
||||
|
||||
constructor(majorVersion: number, height: number, blockBlob: string, seedHash: string) {
|
||||
super();
|
||||
this.majorVersion = majorVersion;
|
||||
this.height = height;
|
||||
this.blockBlob = blockBlob;
|
||||
this.seedHash = seedHash;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'major_version': this.majorVersion,
|
||||
'height': this.height,
|
||||
'blockBlob': this.blockBlob,
|
||||
'seedHash': this.seedHash
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
}
|
24
src/common/request/FlushCacheRequest.ts
Normal file
24
src/common/request/FlushCacheRequest.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class FlushCacheRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'flush_cache';
|
||||
public readonly badTxs: boolean;
|
||||
public readonly badBlocks: boolean;
|
||||
|
||||
constructor(badTxs: boolean = false, badBlocks: boolean = false) {
|
||||
super();
|
||||
this.badTxs = badTxs;
|
||||
this.badBlocks = badBlocks;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'bad_txs': this.badTxs,
|
||||
'bad_blocks': this.badBlocks
|
||||
};
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
21
src/common/request/FlushTxPoolRequest.ts
Normal file
21
src/common/request/FlushTxPoolRequest.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class FlushTxPoolRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'flush_txpool';
|
||||
public txIds: string[];
|
||||
|
||||
constructor(txIds: string[]) {
|
||||
super();
|
||||
this.txIds = txIds;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'txids': this.txIds
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
35
src/common/request/GenerateBlocksRequest.ts
Normal file
35
src/common/request/GenerateBlocksRequest.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GenerateBlocksRequest extends JsonRPCRequest {
|
||||
public override method: string = "generateblocks";
|
||||
public readonly amountOfBlocks: number;
|
||||
public readonly walletAddress: string;
|
||||
public readonly prevBlock: string;
|
||||
public readonly startingNonce: number;
|
||||
|
||||
constructor(amountOfBlocks: number, walletAddress: string, prevBlock: string = '', startingNonce: number = 0) {
|
||||
super();
|
||||
this.amountOfBlocks = amountOfBlocks;
|
||||
this.walletAddress = walletAddress;
|
||||
this.prevBlock = prevBlock;
|
||||
this.startingNonce = startingNonce;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
const params: { [key: string]: any } = {
|
||||
'amount_of_blocks': this.amountOfBlocks,
|
||||
'wallet_address': this.walletAddress,
|
||||
'starting_nonce': this.startingNonce
|
||||
}
|
||||
|
||||
if (this.prevBlock != '') {
|
||||
params['prev_block'] = this.prevBlock;
|
||||
}
|
||||
|
||||
dict['params'] = params;
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
6
src/common/request/GetAlternateChainsRequest.ts
Normal file
6
src/common/request/GetAlternateChainsRequest.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetAlternateChainsRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_alternate_chains';
|
||||
|
||||
}
|
5
src/common/request/GetBansRequest.ts
Normal file
5
src/common/request/GetBansRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBansRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_bans';
|
||||
}
|
5
src/common/request/GetBlockCountRequest.ts
Normal file
5
src/common/request/GetBlockCountRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBlockCountRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = "get_block_count";
|
||||
}
|
19
src/common/request/GetBlockHashRequest.ts
Normal file
19
src/common/request/GetBlockHashRequest.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBlockHashRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = "on_get_block_hash";
|
||||
public readonly blockHeight: number;
|
||||
|
||||
constructor(blockHeight: number) {
|
||||
super();
|
||||
this.blockHeight = blockHeight;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = [this.blockHeight];
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
25
src/common/request/GetBlockHeaderByHashRequest.ts
Normal file
25
src/common/request/GetBlockHeaderByHashRequest.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBlockHeaderByHashRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_block_header_by_hash';
|
||||
public readonly hash: string;
|
||||
public readonly fillPowHash: boolean;
|
||||
|
||||
constructor(hash: string, fillPowHash: boolean = false) {
|
||||
super();
|
||||
|
||||
this.hash = hash;
|
||||
this.fillPowHash = fillPowHash;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'hash': this.hash,
|
||||
'fill_pow_hash': this.fillPowHash
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
24
src/common/request/GetBlockHeaderByHeightRequest.ts
Normal file
24
src/common/request/GetBlockHeaderByHeightRequest.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBlockHeaderByHeightRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_block_header_by_height';
|
||||
public readonly height: number;
|
||||
public readonly fillPowHash: boolean;
|
||||
|
||||
constructor(height: number, fillPowHash: boolean = false) {
|
||||
super();
|
||||
this.height = height;
|
||||
this.fillPowHash = fillPowHash;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'height': this.height,
|
||||
'fill_pow_hash': this.fillPowHash
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
27
src/common/request/GetBlockHeadersRangeRequest.ts
Normal file
27
src/common/request/GetBlockHeadersRangeRequest.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBlockHeadersRangeRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_block_headers_range';
|
||||
public readonly startHeight: number;
|
||||
public readonly endHeight: number;
|
||||
public readonly fillPowHash: boolean;
|
||||
|
||||
constructor(startHeight: number, endHeight: number, fillPowHash: boolean = false) {
|
||||
super();
|
||||
this.startHeight = startHeight;
|
||||
this.endHeight = endHeight;
|
||||
this.fillPowHash = fillPowHash;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'start_height': this.startHeight,
|
||||
'end_height': this.endHeight,
|
||||
'fill_pow_hash': this.fillPowHash
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
24
src/common/request/GetBlockTemplateRequest.ts
Normal file
24
src/common/request/GetBlockTemplateRequest.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetBlockTemplateRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = "get_block_template";
|
||||
public readonly walletAddress: string;
|
||||
public readonly reserveSize: number;
|
||||
|
||||
constructor(walletAddress: string, reserveSize: number) {
|
||||
super();
|
||||
this.walletAddress = walletAddress;
|
||||
this.reserveSize = reserveSize;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'wallet_address': this.walletAddress,
|
||||
'reserve_size': this.reserveSize
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
5
src/common/request/GetConnectionsRequest.ts
Normal file
5
src/common/request/GetConnectionsRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetConnectionsRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_connections';
|
||||
}
|
23
src/common/request/GetFeeEstimateRequest.ts
Normal file
23
src/common/request/GetFeeEstimateRequest.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetFeeEstimateRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_fee_estimate';
|
||||
public readonly graceBlocks: number;
|
||||
|
||||
constructor(graceBlocks: number = 0) {
|
||||
super();
|
||||
this.graceBlocks = graceBlocks;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
if (this.graceBlocks > 0) {
|
||||
dict['params'] = {
|
||||
'grace_blocks': this.graceBlocks
|
||||
};
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
}
|
5
src/common/request/GetInfoRequest.ts
Normal file
5
src/common/request/GetInfoRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetInfoRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_info';
|
||||
}
|
21
src/common/request/GetLastBlockHeaderRequest.ts
Normal file
21
src/common/request/GetLastBlockHeaderRequest.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetLastBlockHeaderRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_last_block_header';
|
||||
public readonly fillPowHash: boolean;
|
||||
|
||||
constructor(fillPowHash: boolean = false) {
|
||||
super();
|
||||
this.fillPowHash = fillPowHash;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'fill_pow_hash': this.fillPowHash
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
7
src/common/request/GetMinerDataRequest.ts
Normal file
7
src/common/request/GetMinerDataRequest.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetMinerDataRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_miner_data';
|
||||
|
||||
|
||||
}
|
33
src/common/request/GetOutputHistogramRequest.ts
Normal file
33
src/common/request/GetOutputHistogramRequest.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetOutputHistogramRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_output_histogram';
|
||||
public readonly amounts: number[];
|
||||
public readonly minCount: number;
|
||||
public readonly maxCount: number;
|
||||
public readonly unlocked: boolean;
|
||||
public readonly recentCutoff: number;
|
||||
|
||||
constructor(amounts: number[], minCount: number, maxCount: number, unlocked: boolean, recentCutoff: number) {
|
||||
super();
|
||||
this.amounts = amounts;
|
||||
this.minCount = minCount;
|
||||
this.maxCount = maxCount;
|
||||
this.unlocked = unlocked;
|
||||
this.recentCutoff = recentCutoff;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'amounts': this.amounts,
|
||||
'min_count': this.minCount,
|
||||
'max_count': this.maxCount,
|
||||
'unlocked': this.unlocked,
|
||||
'recentCutoff': this.recentCutoff
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
5
src/common/request/GetTxPoolBacklogRequest.ts
Normal file
5
src/common/request/GetTxPoolBacklogRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class GetTxPoolBacklogRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_txpool_backlog';
|
||||
}
|
5
src/common/request/GetVersionRequest.ts
Normal file
5
src/common/request/GetVersionRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest"
|
||||
|
||||
export class GetVersionRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'get_version';
|
||||
}
|
5
src/common/request/HardForkInfoRequest.ts
Normal file
5
src/common/request/HardForkInfoRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class HardForkInfoRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'hard_fork_info';
|
||||
}
|
13
src/common/request/JsonRPCRequest.ts
Normal file
13
src/common/request/JsonRPCRequest.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export abstract class JsonRPCRequest {
|
||||
public readonly version: string = "2.0";
|
||||
public readonly id: string = "0";
|
||||
public abstract readonly method: string;
|
||||
|
||||
public toDictionary(): { [key: string]: any } {
|
||||
return {
|
||||
"jsonrpc": this.version,
|
||||
"id": this.id,
|
||||
"method": this.method
|
||||
}
|
||||
}
|
||||
}
|
22
src/common/request/PruneBlockchainRequest.ts
Normal file
22
src/common/request/PruneBlockchainRequest.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class PruneBlockchainRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'prune_blockchain';
|
||||
public readonly check: boolean;
|
||||
|
||||
constructor(check: boolean = false) {
|
||||
super();
|
||||
|
||||
this.check = check;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'check': this.check
|
||||
};
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
21
src/common/request/RelayTxRequest.ts
Normal file
21
src/common/request/RelayTxRequest.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class RelayTxRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'relay_tx';
|
||||
public readonly txIds: string[];
|
||||
|
||||
constructor(txIds: string[]) {
|
||||
super();
|
||||
this.txIds = txIds;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
|
||||
dict['params'] = {
|
||||
'txids': this.txIds
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
26
src/common/request/SetBansRequest.ts
Normal file
26
src/common/request/SetBansRequest.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Ban } from "../Ban";
|
||||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class SetBansRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'set_bans';
|
||||
public bans: Ban[];
|
||||
|
||||
constructor(bans: Ban[]) {
|
||||
super();
|
||||
|
||||
this.bans = bans;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
const dict = super.toDictionary();
|
||||
const bans: { [key: string]: any }[] = []
|
||||
|
||||
this.bans.forEach((ban) => bans.push(ban.toDictionary()));
|
||||
|
||||
dict['params'] = {
|
||||
'bans': bans
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
19
src/common/request/SubmitBlockRequest.ts
Normal file
19
src/common/request/SubmitBlockRequest.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class SubmitBlockRequest extends JsonRPCRequest {
|
||||
public override method: string = "submit_block";
|
||||
public readonly blockBlobData: string[];
|
||||
|
||||
constructor(blockBlobData: string[]) {
|
||||
super();
|
||||
this.blockBlobData = blockBlobData;
|
||||
}
|
||||
|
||||
public override toDictionary(): { [key: string]: any; } {
|
||||
let dict = super.toDictionary();
|
||||
|
||||
dict['params'] = this.blockBlobData;
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
5
src/common/request/SyncInfoRequest.ts
Normal file
5
src/common/request/SyncInfoRequest.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { JsonRPCRequest } from "./JsonRPCRequest";
|
||||
|
||||
export class SyncInfoRequest extends JsonRPCRequest {
|
||||
public override readonly method: string = 'sync_info';
|
||||
}
|
113
src/styles.scss
113
src/styles.scss
|
@ -1,50 +1,81 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
/*
|
||||
body {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
*/
|
||||
$primary: #ff5733;
|
||||
|
||||
@import "/node_modules/bootstrap/scss/bootstrap";
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
background-color: #373636;
|
||||
height: 100%;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
/* CAN (MUST) BE REMOVED ! Sample Global style */
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background: url(./assets/background.jpg) no-repeat center fixed;
|
||||
-webkit-background-size: cover; /* pour anciens Chrome et Safari */
|
||||
background-size: cover; /* version standardisée */
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
margin: 0;
|
||||
padding: 50px 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff !important;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
background: #ed3330;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
transition: all 0.4s ease 0s;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
color: #ed3330 !important;
|
||||
letter-spacing: 1px;
|
||||
-webkit-box-shadow: 0px 5px 40px -10px rgba(0,0,0,0.57);
|
||||
-moz-box-shadow: 0px 5px 40px -10px rgba(0,0,0,0.57);
|
||||
box-shadow: 5px 40px -10px rgba(0,0,0,0.57);
|
||||
transition: all 0.4s ease 0s;
|
||||
}
|
||||
}
|
||||
html {
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
max-height: 100vh;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.dropdown-toggle { outline: 0; }
|
||||
|
||||
.btn-toggle {
|
||||
padding: .25rem .5rem;
|
||||
font-weight: 600;
|
||||
color: var(--bs-emphasis-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
.btn-toggle:hover,
|
||||
.btn-toggle:focus {
|
||||
color: rgba(var(--bs-emphasis-color-rgb), .85);
|
||||
background-color: var(--bs-tertiary-bg);
|
||||
}
|
||||
|
||||
.btn-toggle::before {
|
||||
width: 1.25em;
|
||||
line-height: 0;
|
||||
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
|
||||
transition: transform .35s ease;
|
||||
transform-origin: .5em 50%;
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] .btn-toggle::before {
|
||||
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28255,255,255,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.btn-toggle[aria-expanded="true"] {
|
||||
color: rgba(var(--bs-emphasis-color-rgb), .85);
|
||||
}
|
||||
.btn-toggle[aria-expanded="true"]::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.btn-toggle-nav a {
|
||||
padding: .1875rem .5rem;
|
||||
margin-top: .125rem;
|
||||
margin-left: 1.25rem;
|
||||
}
|
||||
.btn-toggle-nav a:hover,
|
||||
.btn-toggle-nav a:focus {
|
||||
background-color: var(--bs-tertiary-bg);
|
||||
}
|
||||
|
||||
.scrollarea {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background-color: #ff5733; /* Nuovo colore di sfondo */
|
||||
border-color: #ff5733; /* Nuovo colore del bordo */
|
||||
}
|
Loading…
Reference in a new issue