Add preload fix, blockchain operations implementation

This commit is contained in:
everoddandeven 2024-09-30 21:07:57 +02:00
parent 08c2a5a5ee
commit 0d78976e1d
16 changed files with 766 additions and 88 deletions

View file

@ -38,7 +38,8 @@
], ],
"scripts": [ "scripts": [
"node_modules/jquery/dist/jquery.min.js", "node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"node_modules/bootstrap-table/dist/bootstrap-table.min.js"
], ],
"styles": [ "styles": [
"src/styles.scss", "src/styles.scss",

View file

@ -31,9 +31,11 @@ function createWindow(): BrowserWindow {
width: size.width, width: size.width,
height: size.height, height: size.height,
webPreferences: { webPreferences: {
nodeIntegration: true, preload: path.join(__dirname, 'preload.js'),
allowRunningInsecureContent: (serve), nodeIntegration: false,
contextIsolation: false allowRunningInsecureContent: true,
contextIsolation: true,
devTools: true
}, },
icon: path.join(__dirname, 'assets/icons/favicon.ico') icon: path.join(__dirname, 'assets/icons/favicon.ico')
}); });
@ -348,7 +350,7 @@ try {
} }
}); });
ipcMain.on('start-monerod', (event, configFilePath: string[]) => { ipcMain.handle('start-monerod', (event, configFilePath: string[]) => {
startMoneroDaemon(configFilePath); startMoneroDaemon(configFilePath);
}) })

14
app/preload.js Normal file
View file

@ -0,0 +1,14 @@
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
startMonerod: (args) => {
ipcRenderer.invoke('start-monerod', args);
},
onMoneroStdout: (callback) => {
ipcRenderer.on('monero-stdout', callback);
},
onMoneroClose: (callback) => {
ipcRenderer.on('monero-close', callback);
}
});

View file

@ -106,12 +106,18 @@ export class DaemonService {
constructor(private httpClient: HttpClient, private electronService: ElectronService) { constructor(private httpClient: HttpClient, private electronService: ElectronService) {
this.openDbPromise = this.openDatabase(); this.openDbPromise = this.openDatabase();
this.settings = this.loadSettings(); this.settings = this.loadSettings();
const wdw = (window as any);
if (this.electronService.isElectron) { if (this.electronService.isElectron) {
this.electronService.ipcRenderer.on('monero-close', (event, code: number | null) => { this.electronService.ipcRenderer.on('monero-close', (event, code: number | null) => {
this.onClose(); this.onClose();
}); });
} }
else if (wdw.electronAPI && wdw.electronAPI.onMoneroClose) {
wdw.electronAPI.onMoneroClose((event: any, code: number) => {
this.onClose();
});
}
} }
private onClose(): void { private onClose(): void {
@ -250,16 +256,22 @@ export class DaemonService {
return; return;
} }
/*
if (!this.electronService.isElectron) { if (!this.electronService.isElectron) {
console.error("Could not start monero daemon: not electron app"); console.error("Could not start monero daemon: not electron app");
return; return;
} }
*/
this.starting = true; this.starting = true;
console.log("Starting daemon"); console.log("Starting daemon");
const settings = await this.getSettings(); const settings = await this.getSettings();
this.electronService.ipcRenderer.send('start-monerod', settings.toCommandOptions());
if (this.electronService.ipcRenderer) this.electronService.ipcRenderer.send('start-monerod', settings.toCommandOptions());
else {
const wdw = (window as any).electronAPI.startMonerod(settings.toCommandOptions());
}
await this.delay(3000); await this.delay(3000);
@ -348,19 +360,23 @@ export class DaemonService {
public async getLastBlockHeader(fillPowHash: boolean = false): Promise<BlockHeader> { public async getLastBlockHeader(fillPowHash: boolean = false): Promise<BlockHeader> {
const response = await this.callRpc(new GetLastBlockHeaderRequest(fillPowHash)); const response = await this.callRpc(new GetLastBlockHeaderRequest(fillPowHash));
return BlockHeader.parse(response.block_header); if (response.result && response.result.status == 'BUSY') {
throw new CoreIsBusyError();
}
return BlockHeader.parse(response.result.block_header);
} }
public async getBlockHeaderByHash(hash: string, fillPowHash: boolean = false): Promise<BlockHeader> { public async getBlockHeaderByHash(hash: string, fillPowHash: boolean = false): Promise<BlockHeader> {
const response = await this.callRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash)); const response = await this.callRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash));
return BlockHeader.parse(response.block_header); return BlockHeader.parse(response.result.block_header);
} }
public async getBlockHeaderByHeight(height: number, fillPowHash: boolean = false): Promise<BlockHeader> { public async getBlockHeaderByHeight(height: number, fillPowHash: boolean = false): Promise<BlockHeader> {
const response = await this.callRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash)); const response = await this.callRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash));
return BlockHeader.parse(response.block_header); return BlockHeader.parse(response.result.block_header);
} }
public async getBlockHeadersRange(startHeight: number, endHeight: number, fillPowHash: boolean = false): Promise<BlockHeader[]> { public async getBlockHeadersRange(startHeight: number, endHeight: number, fillPowHash: boolean = false): Promise<BlockHeader[]> {
@ -410,7 +426,11 @@ export class DaemonService {
this.raiseRpcError(response.error); this.raiseRpcError(response.error);
} }
const bans: any[] = response.bans; if (!response.result) {
return [];
}
const bans: any[] = response.result.bans;
const result: Ban[] = []; const result: Ban[] = [];
if (bans) bans.forEach((ban: any) => result.push(Ban.parse(ban))); if (bans) bans.forEach((ban: any) => result.push(Ban.parse(ban)));
@ -691,6 +711,7 @@ export class DaemonService {
throw new Error(`Could not stop daemon: ${response.status}`); throw new Error(`Could not stop daemon: ${response.status}`);
} }
/*
if (this.electronService.isElectron) { if (this.electronService.isElectron) {
return; return;
} }
@ -698,6 +719,7 @@ export class DaemonService {
this.daemonRunning = false; this.daemonRunning = false;
this.onDaemonStatusChanged.emit(false); this.onDaemonStatusChanged.emit(false);
this.onDaemonStopEnd.emit(); this.onDaemonStopEnd.emit();
*/
} }
public async setLimit(limitDown: number, limitUp: number): Promise<{ limitDown: number, limitUp: number }> { public async setLimit(limitDown: number, limitUp: number): Promise<{ limitDown: number, limitUp: number }> {

View file

@ -7,7 +7,7 @@ import { ElectronService } from '../electron/electron.service';
export class MoneroInstallerService { export class MoneroInstallerService {
constructor(private electronService: ElectronService) {} constructor(private electronService: ElectronService) {}
downloadMonero(downloadUrl: string, destination: string): Promise<void> { public downloadMonero(downloadUrl: string, destination: string): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.electronService.ipcRenderer.invoke('download-monero', downloadUrl, destination) this.electronService.ipcRenderer.invoke('download-monero', downloadUrl, destination)
.then(() => resolve()) .then(() => resolve())

View file

@ -1,4 +1,18 @@
<table <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Bans</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<ul class="nav nav-pills m-3" id="pills-tab" role="tablist">
@for(navbarLink of navbarLinks; track navbarLink.name) {
<li class="nav-item mr-2" role="presentation">
<button [class]="navbarLink.selected ? 'nav-link active btn-sm' : 'nav-link btn-sm'" [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" [disabled]="navbarLink.disabled">{{navbarLink.name}}</button>
</li>
}
</ul>
</div>
</div>
<div [hidden]="!daemonRunning || daemonChangingStatus">
<table
id="bansTable" id="bansTable"
data-toggle="bansTable" data-toggle="bansTable"
data-toolbar="#toolbar" data-toolbar="#toolbar"
@ -11,4 +25,9 @@
<th data-field="seconds">Seconds</th> <th data-field="seconds">Seconds</th>
</tr> </tr>
</thead> </thead>
</table> </table>
</div>
<app-daemon-not-running></app-daemon-not-running>
<app-daemon-stopping></app-daemon-stopping>

View file

@ -1,7 +1,9 @@
import { AfterViewInit, Component } from '@angular/core'; import { AfterViewInit, Component, NgZone } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { NavbarService } from '../../shared/components/navbar/navbar.service'; import { NavbarService } from '../../shared/components/navbar/navbar.service';
import { DaemonService } from '../../core/services/daemon/daemon.service'; import { DaemonService } from '../../core/services/daemon/daemon.service';
import { NavbarLink } from '../../shared/components/navbar/navbar.model';
@Component({ @Component({
selector: 'app-bans', selector: 'app-bans',
@ -9,14 +11,33 @@ import { DaemonService } from '../../core/services/daemon/daemon.service';
styleUrl: './bans.component.scss' styleUrl: './bans.component.scss'
}) })
export class BansComponent implements AfterViewInit { export class BansComponent implements AfterViewInit {
public readonly navbarLinks: NavbarLink[] = [
constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService) { new NavbarLink('pills-overview-tab', '#pills-overview', 'pills-overview', true, 'Overview', true),
new NavbarLink('pills-set-bans-tab', '#pills-set-bans', 'pills-set-bans', false, 'Set Bans', true)
];
public daemonRunning: boolean = false;
public get daemonChangingStatus(): boolean {
return this.daemonService.stopping || this.daemonService.starting;
}
constructor(private router: Router, private daemonService: DaemonService, private navbarService: NavbarService, private ngZone: NgZone) {
this.router.events.subscribe((event) => { this.router.events.subscribe((event) => {
if (event instanceof NavigationEnd) { if (event instanceof NavigationEnd) {
if (event.url != '/bans') return; if (event.url != '/bans') return;
this.onNavigationEnd(); this.onNavigationEnd();
} }
}) })
this.daemonService.isRunning().then((running) => {
this.daemonRunning = running
});
this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => {
this.daemonRunning = running;
if (running) this.load();
});
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
@ -24,15 +45,21 @@ export class BansComponent implements AfterViewInit {
console.log('BansComponent AFTER VIEW INIT'); console.log('BansComponent AFTER VIEW INIT');
setTimeout(() => { this.ngZone.run(() => {
//const $ = require('jquery');
//const bootstrapTable = require('bootstrap-table');
const $table = $('#bansTable'); const $table = $('#bansTable');
$table.bootstrapTable({}); $table.bootstrapTable({
});
$table.bootstrapTable('refreshOptions', { $table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped' classes: 'table table-bordered table-hover table-dark table-striped'
}); });
$table.bootstrapTable('showLoading');
this.load(); this.load();
}, 500); });
} }
private onNavigationEnd(): void { private onNavigationEnd(): void {
@ -50,9 +77,13 @@ export class BansComponent implements AfterViewInit {
'host': ban.host, 'host': ban.host,
'seconds': ban.seconds 'seconds': ban.seconds
})); }));
$table.bootstrapTable('hideLoading');
$table.bootstrapTable('load', bans); $table.bootstrapTable('load', bans);
} }
public async setBans() {
}
} }

View file

@ -11,6 +11,449 @@
</ul> </ul>
</div> </div>
</div> </div>
<div *ngIf="daemonRunning" class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-last-block-header" role="tabpanel" aria-labelledby="pills-last-block-header-tab" tabindex="0">
<div *ngIf="getLastBlockError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>&nbsp;&nbsp;
<div>
{{getLastBlockError}}
</div>
</div>
<div *ngIf="getLastBlockError == ''" class="card p-1">
<div class="card-header bg-primary text-white d-flex">
<h4>Block Header Details</h4>
</div>
<div class="card-body">
@if(lastBlockHeader) {
<div class="row">
<div class="col-md-6">
<h6>Block Information</h6>
<ul class="list-group">
<li class="list-group-item"><strong>Block Size:</strong> {{ lastBlockHeader.blockSize }} bytes</li>
<li class="list-group-item"><strong>Block Weight:</strong> {{ lastBlockHeader.blockWeight }} units</li>
<li class="list-group-item"><strong>Height:</strong> {{ lastBlockHeader.height }}</li>
<li class="list-group-item"><strong>Major Version:</strong> {{ lastBlockHeader.majorVersion }}</li>
<li class="list-group-item"><strong>Minor Version:</strong> {{ lastBlockHeader.minorVersion }}</li>
<li class="list-group-item"><strong>Nonce:</strong> {{ lastBlockHeader.nonce }}</li>
<li class="list-group-item"><strong>Number of Transactions:</strong> {{ lastBlockHeader.numTxes }}</li>
<li class="list-group-item"><strong>Reward:</strong> {{ lastBlockHeader.reward }} XMR</li>
<li class="list-group-item"><strong>Timestamp:</strong> {{ lastBlockHeader.timestamp | date:'medium' }}</li>
</ul>
</div>
<div class="col-md-6">
<h6>Hashes & Difficulty</h6>
<ul class="list-group">
<li class="list-group-item"><strong>Block Hash:</strong> {{ lastBlockHeader.hash }}</li>
<li class="list-group-item"><strong>Previous Hash:</strong> {{ lastBlockHeader.prevHash }}</li>
<li class="list-group-item"><strong>PoW Hash:</strong> {{ lastBlockHeader.powHash }}</li>
<li class="list-group-item"><strong>Miner Transaction Hash:</strong> {{ lastBlockHeader.minerTxHash }}</li>
<li class="list-group-item"><strong>Cumulative Difficulty:</strong> {{ lastBlockHeader.cumulativeDifficulty }}</li>
<li class="list-group-item"><strong>Wide Cumulative Difficulty:</strong> {{ lastBlockHeader.wideCumulativeDifficulty }}</li>
<li class="list-group-item"><strong>Difficulty:</strong> {{ lastBlockHeader.difficulty }}</li>
<li class="list-group-item"><strong>Wide Difficulty:</strong> {{ lastBlockHeader.wideDifficulty }}</li>
</ul>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h6>Miscellaneous</h6>
<ul class="list-group">
<li class="list-group-item"><strong>Orphan Status:</strong> {{ lastBlockHeader.orphanStatus ? 'Yes' : 'No' }}</li>
<li class="list-group-item"><strong>Depth:</strong> {{ lastBlockHeader.depth }}</li>
<li class="list-group-item"><strong>Long Term Weight:</strong> {{ lastBlockHeader.longTermWeight }}</li>
</ul>
</div>
</div>
}
@else {
<div class="row">
<div class="col-md-6">
<h6>Block Information</h6>
<ul class="list-group">
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
</ul>
</div>
<div class="col-md-6">
<h6>Hashes & Difficulty</h6>
<ul class="list-group">
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
</ul>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h6>Miscellaneous</h6>
<ul class="list-group">
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
</ul>
</div>
</div>
}
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-get-block" role="tabpanel" aria-labelledby="pills-get-block-tab" tabindex="0">
<div *ngIf="getBlockError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>&nbsp;&nbsp;
<div>
{{getBlockError}}
</div>
</div>
<div *ngIf="block" class="card">
<div class="card-header bg-dark text-white">
<h4>Block Details</h4>
</div>
<div class="card-body">
<!-- Placeholder mentre i dati sono in caricamento -->
<ng-container *ngIf="block && !gettingLastBlock; else loading">
<div class="row">
<div class="col-md-12">
<h6>Block Blob</h6>
<p class="text-break bg-dark p-2">{{ block.blob }}</p>
</div>
</div>
<!-- Dettagli del BlockHeader -->
<div class="row mt-4">
<div class="col-md-12">
<h6>Block Header</h6>
<div class="card">
<div class="card-body">
<ul class="list-group">
<li class="list-group-item"><strong>Block Size:</strong> {{ block.blockHeader.blockSize }} bytes</li>
<li class="list-group-item"><strong>Block Weight:</strong> {{ block.blockHeader.blockWeight }} units</li>
<li class="list-group-item"><strong>Height:</strong> {{ block.blockHeader.height }}</li>
<li class="list-group-item"><strong>Hash:</strong> {{ block.blockHeader.hash }}</li>
<li class="list-group-item"><strong>Previous Hash:</strong> {{ block.blockHeader.prevHash }}</li>
<li class="list-group-item"><strong>Miner Transaction Hash:</strong> {{ block.blockHeader.minerTxHash }}</li>
<li class="list-group-item"><strong>Difficulty:</strong> {{ block.blockHeader.difficulty }}</li>
<li class="list-group-item"><strong>Timestamp:</strong> {{ block.blockHeader.timestamp | date:'medium' }}</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Dettagli del BlockDetails -->
<div class="row mt-4">
<div class="col-md-12">
<h6>Block Additional Details</h6>
<div class="card">
<div class="card-body">
<ul class="list-group">
<li class="list-group-item"><strong>Major Version:</strong> {{ block.details.majorVersion }}</li>
<li class="list-group-item"><strong>Minor Version:</strong> {{ block.details.minorVersion }}</li>
<li class="list-group-item"><strong>Timestamp:</strong> {{ block.details.timestamp | date:'medium' }}</li>
<li class="list-group-item"><strong>Previous ID:</strong> {{ block.details.prevId }}</li>
<li class="list-group-item"><strong>Nonce:</strong> {{ block.details.nonce }}</li>
<li class="list-group-item"><strong>Transaction Hashes:</strong>
<ul>
<li *ngFor="let hash of block.details.txHashes">{{ hash }}</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</ng-container>
<!-- Placeholder di caricamento -->
<ng-template #loading>
<div class="row">
<div class="col-md-12">
<h6>Block Blob</h6>
<p class="placeholder-glow bg-light p-2"><span class="placeholder col-8"></span></p>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h6>Block Header</h6>
<div class="card">
<div class="card-body">
<ul class="list-group">
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
</ul>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h6>Block Additional Details</h6>
<div class="card">
<div class="card-body">
<ul class="list-group">
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder-glow"><span class="placeholder col-6"></span></li>
</ul>
</div>
</div>
</div>
</div>
</ng-template>
</div>
</div>
<hr *ngIf="block" class="my-4">
<div class="row g-5 p-2">
<div class="cold-md-7 col-lg-12">
<div class="row gy-3">
<div class="form-check form-switch col-md-6">
<label for="get-block-by-hash" class="form-check-label">Look up by hash</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="get-block-by-hash" [checked]="getBlockByHash" [(ngModel)]="getBlockByHash" [ngModelOptions]="{standalone: true}">
<br>
<small class="text-body-secondary">Get block information by hash</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="fill-pow-hash" class="form-check-label">Fill PoW hash</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="fill-pow-hash" [checked]="fillPoWHash" [(ngModel)]="fillPoWHash" [ngModelOptions]="{standalone: true}">
<br>
<small class="text-body-secondary">Add PoW hash to block header response</small>
</div>
<div *ngIf="getBlockByHash" class="col-sm-6">
<label for="get-block-hash" class="form-label">Block hash</label>
<input type="text" class="form-control" id="get-block-hash" placeholder="" [(ngModel)]="getBlockHash" [ngModelOptions]="{standalone: true}" [disabled]="!getBlockByHash">
<small class="text-body-secondary">Block hash</small>
</div>
<div *ngIf="!getBlockByHash" class="col-sm-3">
<label for="get-block-height" class="form-label">Block height</label>
<input type="number" class="form-control" id="get-block-height" placeholder="" [(ngModel)]="getBlockHeight" [ngModelOptions]="{standalone: true}" [disabled]="getBlockByHash">
<small class="text-body-secondary">Block height</small>
</div>
</div>
</div>
</div>
<hr class="my-4">
<button *ngIf="!gettingLastBlock" class="w-100 btn btn-primary btn-lg" type="button" (click)="getBlock()">Get Block</button>
<button *ngIf="gettingLastBlock" class="w-100 btn btn-primary btn-lg" type="button" disabled>Getting Block ...</button>
</div>
<div class="tab-pane fade" id="pills-get-block-header" role="tabpanel" aria-labelledby="pills-get-block-header-tab" tabindex="0">
<div *ngIf="blockHeader" class="card p-1">
<div class="card-header bg-primary text-white d-flex">
<h4>Block Header Details</h4>
</div>
<div class="card-body">
@if(blockHeader) {
<div class="row">
<div class="col-md-6">
<h6>Block Information</h6>
<ul class="list-group">
<li class="list-group-item"><strong>Block Size:</strong> {{ blockHeader.blockSize }} bytes</li>
<li class="list-group-item"><strong>Block Weight:</strong> {{ blockHeader.blockWeight }} units</li>
<li class="list-group-item"><strong>Height:</strong> {{ blockHeader.height }}</li>
<li class="list-group-item"><strong>Major Version:</strong> {{ blockHeader.majorVersion }}</li>
<li class="list-group-item"><strong>Minor Version:</strong> {{ blockHeader.minorVersion }}</li>
<li class="list-group-item"><strong>Nonce:</strong> {{ blockHeader.nonce }}</li>
<li class="list-group-item"><strong>Number of Transactions:</strong> {{ blockHeader.numTxes }}</li>
<li class="list-group-item"><strong>Reward:</strong> {{ blockHeader.reward }} XMR</li>
<li class="list-group-item"><strong>Timestamp:</strong> {{ blockHeader.timestamp | date:'medium' }}</li>
</ul>
</div>
<div class="col-md-6">
<h6>Hashes & Difficulty</h6>
<ul class="list-group">
<li class="list-group-item"><strong>Block Hash:</strong> {{ blockHeader.hash }}</li>
<li class="list-group-item"><strong>Previous Hash:</strong> {{ blockHeader.prevHash }}</li>
<li class="list-group-item"><strong>PoW Hash:</strong> {{ blockHeader.powHash }}</li>
<li class="list-group-item"><strong>Miner Transaction Hash:</strong> {{ blockHeader.minerTxHash }}</li>
<li class="list-group-item"><strong>Cumulative Difficulty:</strong> {{ blockHeader.cumulativeDifficulty }}</li>
<li class="list-group-item"><strong>Wide Cumulative Difficulty:</strong> {{ blockHeader.wideCumulativeDifficulty }}</li>
<li class="list-group-item"><strong>Difficulty:</strong> {{ blockHeader.difficulty }}</li>
<li class="list-group-item"><strong>Wide Difficulty:</strong> {{ blockHeader.wideDifficulty }}</li>
</ul>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h6>Miscellaneous</h6>
<ul class="list-group">
<li class="list-group-item"><strong>Orphan Status:</strong> {{ blockHeader.orphanStatus ? 'Yes' : 'No' }}</li>
<li class="list-group-item"><strong>Depth:</strong> {{ blockHeader.depth }}</li>
<li class="list-group-item"><strong>Long Term Weight:</strong> {{ blockHeader.longTermWeight }}</li>
</ul>
</div>
</div>
}
@else {
<div class="row">
<div class="col-md-6">
<h6>Block Information</h6>
<ul class="list-group">
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
</ul>
</div>
<div class="col-md-6">
<h6>Hashes & Difficulty</h6>
<ul class="list-group">
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
</ul>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h6>Miscellaneous</h6>
<ul class="list-group">
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
<li class="list-group-item placeholder col-7"><span class="placeholder col-6"></span></li>
</ul>
</div>
</div>
}
</div>
</div>
<hr *ngIf="blockHeader" class="my-4">
<div class="row g-5 p-2">
<div class="cold-md-7 col-lg-12">
<div class="row gy-3">
<div class="form-check form-switch col-md-6">
<label for="get-block-header-by-hash" class="form-check-label">Look up by hash</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="get-block-header-by-hash" [checked]="getBlockHeaderByHash" [(ngModel)]="getBlockHeaderByHash" [ngModelOptions]="{standalone: true}">
<br>
<small class="text-body-secondary">Get block header information by hash</small>
</div>
<div class="form-check form-switch col-md-6">
<label for="fill-pow-hash" class="form-check-label">Fill PoW hash</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="fill-pow-hash" [checked]="fillPoWHash" [(ngModel)]="fillPoWHash" [ngModelOptions]="{standalone: true}">
<br>
<small class="text-body-secondary">Add PoW hash to block header response</small>
</div>
<div *ngIf="getBlockHeaderByHash" class="col-sm-6">
<label for="get-block-header-hash" class="form-label">Block header hash</label>
<input type="text" class="form-control" id="get-block-header-hash" placeholder="" [(ngModel)]="getBlockHeaderHash" [ngModelOptions]="{standalone: true}" [disabled]="!getBlockHeaderByHash">
<small class="text-body-secondary">Block header hash</small>
</div>
<div *ngIf="!getBlockHeaderByHash" class="col-sm-3">
<label for="get-block-header-height" class="form-label">Block header height</label>
<input type="number" class="form-control" id="get-block-header-height" placeholder="" [(ngModel)]="getBlockHeaderHeight" [ngModelOptions]="{standalone: true}" [disabled]="getBlockHeaderByHash">
<small class="text-body-secondary">Block header height</small>
</div>
</div>
</div>
</div>
<hr class="my-4">
<button *ngIf="!gettingBlockHeader" class="w-100 btn btn-primary btn-lg" type="button" (click)="getBlockHeader()">Get Block Header</button>
<button *ngIf="gettingBlockHeader" class="w-100 btn btn-primary btn-lg" type="button" disabled>Getting Block Header ...</button>
</div>
<div class="tab-pane fade" id="pills-pop-blocks" role="tabpanel" aria-labelledby="pills-pop-blocks-tab" tabindex="0">
<div *ngIf="popBlocksError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>&nbsp;&nbsp;
<div>
{{popBlocksError}}
</div>
</div>
<div *ngIf="popBlocksResult != undefined" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-check-circle m-2"></i></h4>&nbsp;&nbsp;
<div>
New blockchain height: {{popBlocksResult}}
</div>
</div>
<div class="row g-5 p-2">
<div class="cold-md-7 col-lg-12">
<div class="row gy-3">
<div class="col-sm-6">
<label for="pop-blocks-nblocks" class="form-label">Blocks to pop</label>
<input type="number" class="form-control" id="pop-blocks-nblocks" placeholder="" [(ngModel)]="popBlocksNBlocks" [ngModelOptions]="{standalone: true}" [disabled]="getBlockHeaderByHash">
<small class="text-body-secondary">Number of blocks top pop from blockchain</small>
</div>
</div>
</div>
</div>
<hr class="my-4">
<button *ngIf="!poppingBlocks" class="w-100 btn btn-primary btn-lg" type="button" (click)="popBlocks()">Pop Blocks</button>
<button *ngIf="poppingBlocks" class="w-100 btn btn-primary btn-lg" type="button" disabled>Popping Blocks ...</button>
</div>
<div class="tab-pane fade" id="pills-save-bc" role="tabpanel" aria-labelledby="pills-save-bc-tab" tabindex="0">
<div *ngIf="saveBlockchainError != ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>&nbsp;&nbsp;
<div>
{{saveBlockchainError}}
</div>
</div>
<div *ngIf="blockchainSaved" class="alert alert-success d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-check-circle m-2"></i></h4>&nbsp;&nbsp;
<div>
Successfully saved blockchain
</div>
</div>
<div class="alert alert-info d-flex text-center" role="alert">
<div>
The blockchain does not need saving and is always saved when modified, however it does a sync to flush the filesystem cache onto the disk for safety purposes against Operating System or Hardware crashes.
</div>
</div>
<hr class="my-4">
<button *ngIf="!savingBlockchain" class="w-100 btn btn-primary btn-lg" type="button" (click)="saveBlockchain()">Save Blockchain</button>
<button *ngIf="savingBlockchain" class="w-100 btn btn-primary btn-lg" type="button" disabled>Saving Blockchain ...</button>
</div>
</div>
<app-daemon-not-running></app-daemon-not-running> <app-daemon-not-running></app-daemon-not-running>
<app-daemon-stopping></app-daemon-stopping> <app-daemon-stopping></app-daemon-stopping>

View file

@ -0,0 +1,8 @@
.card-header {
background-color: #007bff;
color: white;
}
.placeholder-glow .placeholder {
background-color: rgba(0, 0, 0, 0.1);
}

View file

@ -1,38 +1,151 @@
import { Component, NgZone } from '@angular/core'; import { AfterViewInit, Component, NgZone } from '@angular/core';
import { NavbarLink } from '../../shared/components/navbar/navbar.model'; import { NavbarLink } from '../../shared/components/navbar/navbar.model';
import { DaemonService } from '../../core/services/daemon/daemon.service'; import { DaemonService } from '../../core/services/daemon/daemon.service';
import { Block, BlockHeader } from '../../../common';
@Component({ @Component({
selector: 'app-blockchain', selector: 'app-blockchain',
templateUrl: './blockchain.component.html', templateUrl: './blockchain.component.html',
styleUrl: './blockchain.component.scss' styleUrl: './blockchain.component.scss'
}) })
export class BlockchainComponent { export class BlockchainComponent implements AfterViewInit {
public readonly navbarLinks: NavbarLink[]; public readonly navbarLinks: NavbarLink[];
public daemonRunning: boolean = false; public daemonRunning: boolean = false;
public lastBlockHeader?: BlockHeader;
public getLastBlockError: string = '';
public block?: Block;
public getBlockByHash: boolean = false;
public getBlockHash: string = '';
public getBlockHeight: number = 0;
public fillPoWHash: boolean = false;
public gettingLastBlock: boolean = false;
public gettingBlock: boolean = false;
public blockHeader?: BlockHeader;
public getBlockHeaderByHash: boolean = false;
public getBlockHeaderHash: string = '';
public getBlockHeaderHeight: number = 0;
public getBlockHeaderError: string = '';
public getBlockError: string = '';
public gettingBlockHeader: boolean = false;
public popBlocksNBlocks: number = 0;
public poppingBlocks: boolean = false;
public popBlocksError: string = '';
public popBlocksResult?: number;
public savingBlockchain: boolean = false;
public saveBlockchainError: string = '';
public blockchainSaved: boolean = false;
constructor(private daemonService: DaemonService, private ngZone: NgZone) { constructor(private daemonService: DaemonService, private ngZone: NgZone) {
this.navbarLinks = [ this.navbarLinks = [
new NavbarLink('pills-last-block-header-tab', '#pills-last-block-header', 'pills-last-block-header', false, 'Last Block Header', true), new NavbarLink('pills-last-block-header-tab', '#pills-last-block-header', 'pills-last-block-header', true, 'Last Block Header', true),
new NavbarLink('pills-get-block-tab', '#pills-get-block', 'pills-get-block', false, 'Get Block', true), new NavbarLink('pills-get-block-tab', '#pills-get-block', 'pills-get-block', false, 'Get Block', true),
new NavbarLink('pills-get-block-header-tab', '#pills-get-block-header', 'pills-get-block-header', false, 'Get Block Header', true), new NavbarLink('pills-get-block-header-tab', '#pills-get-block-header', 'pills-get-block-header', false, 'Get Block Header', true),
new NavbarLink('pills-pop-blocks-tab', '#pills-pop-blocks', 'pills-pop-blocks', false, 'Pop Blocks', true), new NavbarLink('pills-pop-blocks-tab', '#pills-pop-blocks', 'pills-pop-blocks', false, 'Pop Blocks', true),
new NavbarLink('pills-save-bc-tab', '#pills-save-bc', 'pills-save-bc', false, 'Save Blockchain', true) new NavbarLink('pills-save-bc-tab', '#pills-save-bc', 'pills-save-bc', false, 'Save Blockchain', true)
] ];
this.daemonService.isRunning().then((result: boolean) => this.daemonRunning = result);
this.daemonService.onDaemonStatusChanged.subscribe((running) => { this.daemonService.onDaemonStatusChanged.subscribe((running) => {
this.ngZone.run(() => { this.ngZone.run(() => {
this.daemonRunning = running; this.daemonRunning = running;
this.navbarLinks.forEach((link) => link.disabled = !running);
}); });
}); });
this.daemonService.isRunning().then((value: boolean) => { this.daemonService.isRunning().then((value: boolean) => {
this.ngZone.run(() => { this.ngZone.run(() => {
this.daemonRunning = value; this.daemonRunning = value;
this.navbarLinks.forEach((link) => link.disabled = !value);
}); });
}); });
} }
ngAfterViewInit(): void {
this.load();
}
public async load(): Promise<void> {
await this.getLastBlockHeader();
}
private async getLastBlockHeader(): Promise<void> {
this.gettingLastBlock = true;
try {
this.lastBlockHeader = await this.daemonService.getLastBlockHeader(true);
this.getLastBlockError = '';
}
catch(error) {
console.error(error);
this.getLastBlockError = `${error}`;
}
this.gettingLastBlock = false;
}
public async getBlock(): Promise<void> {
this.gettingLastBlock = true;
try {
this.block = await this.daemonService.getBlock(this.getBlockByHash ? this.getBlockHash : this.getBlockHeight, this.fillPoWHash);
this.getBlockError = '';
}
catch(error) {
console.error(error);
this.getBlockError = `${error}`;
}
this.gettingLastBlock = false;
}
public async getBlockHeader(): Promise<void> {
this.gettingBlockHeader = true;
try {
if (this.getBlockHeaderByHash) {
this.blockHeader = await this.daemonService.getBlockHeaderByHash(this.getBlockHeaderHash, this.fillPoWHash);
}
else {
this.blockHeader = await this.daemonService.getBlockHeaderByHeight(this.getBlockHeaderHeight, this.fillPoWHash);
}
this.getBlockHeaderError = '';
} catch (error) {
console.error(error);
this.getBlockHeaderError = `${error}`;
}
this.gettingBlockHeader = false;
}
public async popBlocks(): Promise<void> {
this.poppingBlocks = true;
try {
this.popBlocksResult = await this.daemonService.popBlocks(this.popBlocksNBlocks);
this.popBlocksError = '';
}
catch(error) {
console.error(error);
this.popBlocksResult = undefined;
this.popBlocksError = `${error}`;
}
this.poppingBlocks = false;
}
public async saveBlockchain(): Promise<void> {
this.savingBlockchain = true;
try {
await this.daemonService.saveBc();
this.blockchainSaved = true;
}
catch(error) {
console.error(error);
this.blockchainSaved = false;
this.saveBlockchainError = `${error}`;
}
this.savingBlockchain = false;
}
} }

View file

@ -7,8 +7,7 @@ import { NavbarService } from '../../shared/components/navbar/navbar.service';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { DaemonInfo } from '../../../common/DaemonInfo'; import { DaemonInfo } from '../../../common/DaemonInfo';
import * as $ from 'jquery';
import * as bootstrapTable from 'bootstrap-table';
import { LogsService } from '../logs/logs.service'; import { LogsService } from '../logs/logs.service';
import { ElectronService } from '../../core/services'; import { ElectronService } from '../../core/services';
@ -75,7 +74,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
this.nodeType = 'unknown'; this.nodeType = 'unknown';
this.blockchainSize = '0 GB'; this.blockchainSize = '0 GB';
this.syncProgress = '0 %'; this.syncProgress = '0 %';
this.isLoading = true; this.isLoading = false;
this.navbarLinks = [ this.navbarLinks = [
new NavbarLink('pills-home-tab', '#pills-home', 'pills-home', true, 'Overview', true), new NavbarLink('pills-home-tab', '#pills-home', 'pills-home', true, 'Overview', true),
@ -104,18 +103,38 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
console.log('DetailComponent INIT'); console.log('DetailComponent INIT');
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
console.log('DetailComponent AFTER VIEW INIT'); console.log('DetailComponent AFTER VIEW INIT');
this.navbarService.setLinks(this.navbarLinks); this.navbarService.setLinks(this.navbarLinks);
setTimeout(() => {
this.ngZone.run(() => {
const $table = $('#table');
$table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
$table.bootstrapTable('showLoading');
/*
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
*/
});
}, 1000);
if (this.loadInterval != null) return; if (this.loadInterval != null) return;
this.load().then(() => { this.ngZone.run(() => {
this.cards = this.createCards(); this.load().then(() => {
this.cards = this.createCards();
});
}); });
this.loadInterval = setInterval(() => { this.loadInterval = setInterval(() => {
/* /*
const $table = $('#table'); const $table = $('#table');
@ -124,13 +143,17 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
classes: 'table table-bordered table-hover table-dark table-striped' classes: 'table table-bordered table-hover table-dark table-striped'
}); });
*/ */
if (this.stoppingDaemon) return; if (this.stoppingDaemon) return;
this.load().then(() => { this.ngZone.run(() => {
this.cards = this.createCards();
}); this.load().then(() => {
}, 5000); this.cards = this.createCards();
});
}, 5000);
})
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -190,13 +213,6 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
if(!this.electronService.isElectron) this.stoppingDaemon = false; if(!this.electronService.isElectron) this.stoppingDaemon = false;
} }
private onNavigationEnd(): void {
this.load().then(() => {
//this.cards = this.createCards();
});
}
private createLoadingCards(): Card[] { private createLoadingCards(): Card[] {
return [ return [
new Card('Connection Status', this.connectionStatus, true), new Card('Connection Status', this.connectionStatus, true),
@ -238,6 +254,10 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
} }
private async load(): Promise<void> { private async load(): Promise<void> {
if (this.isLoading) {
return;
}
try { try {
this.isLoading = true; this.isLoading = true;
this.daemonRunning = await this.daemonService.isRunning(); this.daemonRunning = await this.daemonService.isRunning();
@ -251,6 +271,11 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
this.navbarService.enableLinks(); this.navbarService.enableLinks();
const $table = $('#table'); const $table = $('#table');
$table.bootstrapTable({});
$table.bootstrapTable('refreshOptions', {
classes: 'table table-bordered table-hover table-dark table-striped'
});
if (this.getPeers().length == 0) $table.bootstrapTable('showLoading');
this.syncInfo = await this.daemonService.syncInfo(); this.syncInfo = await this.daemonService.syncInfo();
this.height = this.syncInfo.height; this.height = this.syncInfo.height;
@ -293,6 +318,8 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
//const blockchainPruned = false; //const blockchainPruned = false;
this.nodeType = blockchainPruned ? 'pruned' : 'full'; this.nodeType = blockchainPruned ? 'pruned' : 'full';
$table.bootstrapTable('load', this.getPeers()); $table.bootstrapTable('load', this.getPeers());
$table.bootstrapTable('hideLoading');
} }
catch(error) { catch(error) {
console.error(error); console.error(error);

View file

@ -19,23 +19,22 @@ export class LogsComponent implements AfterViewInit {
} }
private scrollToBottom(): void { private scrollToBottom(): void {
this.lines; this.ngZone.run(() => {
const terminalOutput = <HTMLDivElement | null>document.getElementById('terminalOutput'); this.lines;
if (terminalOutput) { const terminalOutput = <HTMLDivElement | null>document.getElementById('terminalOutput');
terminalOutput.style.width = `${window.innerWidth}`; if (terminalOutput) {
console.log(`scrolling from ${terminalOutput.offsetTop} to ${terminalOutput.scrollHeight}`) terminalOutput.style.width = `${window.innerWidth}`;
terminalOutput.scrollBy(0, terminalOutput.scrollHeight) console.log(`scrolling from ${terminalOutput.offsetTop} to ${terminalOutput.scrollHeight}`)
} terminalOutput.scrollBy(0, terminalOutput.scrollHeight)
}
});
} }
private onLog(): void { private onLog(): void {
if (this.logTerminal) this.logTerminal.nativeElement.scrollTop = this.logTerminal.nativeElement.scrollHeight; if (this.logTerminal) this.logTerminal.nativeElement.scrollTop = this.logTerminal.nativeElement.scrollHeight;
// Scorri automaticamente in basso // Scorri automaticamente in basso
setTimeout(() => { setTimeout(() => {
this.ngZone.run(() => { this.scrollToBottom();
this.scrollToBottom();
})
}, 100); }, 100);
} }
@ -45,6 +44,8 @@ export class LogsComponent implements AfterViewInit {
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.navbarService.removeLinks(); this.navbarService.removeLinks();
this.scrollToBottom();
} setTimeout(() => {
this.scrollToBottom();
}, 500); }
} }

View file

@ -11,10 +11,16 @@ export class LogsService {
private readonly ansiRegex: RegExp = /\u001b\[[0-9;]*m/g; private readonly ansiRegex: RegExp = /\u001b\[[0-9;]*m/g;
constructor(private electronService: ElectronService, private ngZone: NgZone) { constructor(private electronService: ElectronService, private ngZone: NgZone) {
const wdw = (window as any);
if (this.electronService.isElectron) { if (this.electronService.isElectron) {
this.electronService.ipcRenderer.on('monero-stdout', (event, message: string) => this.log(message)); this.electronService.ipcRenderer.on('monero-stdout', (event, message: string) => this.log(message));
this.electronService.ipcRenderer.on('monero-stderr', (event, message: string) => this.log(message)); this.electronService.ipcRenderer.on('monero-stderr', (event, message: string) => this.log(message));
} }
else if (wdw.electronAPI && wdw.electronAPI.onMoneroStdout) {
wdw.electronAPI.onMoneroStdout((event: any, message: string) => {
this.log(message);
});
}
} }

View file

@ -25,8 +25,6 @@
}, },
*/ */
import { ThisReceiver } from "@angular/compiler";
export class BlockHeader { export class BlockHeader {
public readonly blockSize: number; public readonly blockSize: number;
public readonly blockWeight: number; public readonly blockWeight: number;

View file

@ -4,9 +4,9 @@ export class MinerTx {
public readonly vin: TxInput[]; public readonly vin: TxInput[];
public readonly vout: TxOutput[]; public readonly vout: TxOutput[];
public readonly extra: number[]; public readonly extra: number[];
public readonly rctSignatures: RctSignatures; public readonly rctSignatures?: RctSignatures;
constructor(version: number, unlockTime: number, vin: TxInput[], vout: TxOutput[], extra: number[], rctSignatures: RctSignatures) { constructor(version: number, unlockTime: number, vin: TxInput[], vout: TxOutput[], extra: number[], rctSignatures?: RctSignatures) {
this.version = version; this.version = version;
this.unlockTime = unlockTime; this.unlockTime = unlockTime;
this.vin = vin; this.vin = vin;
@ -21,8 +21,12 @@ export class MinerTx {
const _vin: any[] | undefined = minerTx.vin; const _vin: any[] | undefined = minerTx.vin;
const _vout: any[] | undefined = minerTx.vout; const _vout: any[] | undefined = minerTx.vout;
const extra = minerTx.extra; const extra = minerTx.extra;
const rctSignatures = RctSignatures.parse(minerTx.rct_signatures); let rctSignatures;
if (minerTx.rct_signatures) {
rctSignatures = RctSignatures.parse(minerTx.rct_signatures);
}
const vin: TxInput[] = []; const vin: TxInput[] = [];
const vout: TxOutput[] = []; const vout: TxOutput[] = [];
@ -93,33 +97,20 @@ export class RctSignatures {
} }
export class TxOutputTarget { export class TxOutputTarget {
public readonly taggedKey: TaggedKey; public readonly viewKey: string;
constructor(taggedKey: TaggedKey)
{
this.taggedKey = taggedKey;
}
public static parse(target: any): TxOutputTarget {
const taggedKey = TaggedKey.parse(target.tagged_key);
return new TxOutputTarget(taggedKey);
}
}
export class TaggedKey {
public readonly key: string;
public readonly viewTag: string; public readonly viewTag: string;
constructor(key: string, viewTag: string) { constructor(viewKey: string, viewTag: string)
this.key = key; {
this.viewKey = viewKey;
this.viewTag = viewTag; this.viewTag = viewTag;
} }
public static parse(taggedKey: any): TaggedKey { public static parse(target: any): TxOutputTarget {
const key = taggedKey.key; const viewKey = target.view_key ? target.view_key : '';
const viewTag = taggedKey.view_tag; const viewTag = target.view_tag ? target.view_tag : '';
return new TaggedKey(key, viewTag); return new TxOutputTarget(viewKey, viewTag);
} }
} }

View file

@ -53,5 +53,7 @@ import 'zone.js'; // Included with Angular CLI.
*/ */
import 'jquery'; import 'jquery';
import * as $$ from 'jquery';
import * as $ from 'jquery'; import * as $ from 'jquery';
import 'bootstrap-table'; import * as bootstrapTable from 'bootstrap-table';
//import 'bootstrap-table';