Non json rpc requests implementation

This commit is contained in:
everoddandeven 2024-09-24 22:54:48 +02:00
parent 696d0b5c86
commit bb694c5d7a
59 changed files with 1834 additions and 197 deletions

View file

@ -96,22 +96,13 @@ function execMoneroDaemon(configFilePath: string): ChildProcess {
return monerodProcess; return monerodProcess;
} }
function startMoneroDaemon(configFilePath: string): ChildProcessWithoutNullStreams { function startMoneroDaemon(commandOptions: string[]): ChildProcessWithoutNullStreams {
const monerodPath = path.resolve(__dirname, monerodFilePath); const monerodPath = path.resolve(__dirname, monerodFilePath);
const args = [ console.log("Starting monerod daemon with options: " + commandOptions.join(" "));
'--testnet',
'--fast-block-sync', '1',
'--prune-blockchain',
'--sync-pruned-blocks',
'--confirm-external-bind',
'--max-concurrency', '1',
'--log-level', '1',
'--rpc-access-control-origins=*'
];
// Avvia il processo usando spawn // Avvia il processo usando spawn
const monerodProcess = spawn(monerodPath, args); const monerodProcess = spawn(monerodPath, commandOptions);
// Gestisci l'output di stdout in streaming // Gestisci l'output di stdout in streaming
monerodProcess.stdout.on('data', (data) => { monerodProcess.stdout.on('data', (data) => {

View file

@ -7,6 +7,7 @@ import { DetailRoutingModule } from './pages/detail/detail-routing.module';
import { HardForkInfoRoutingModule } from './pages/hard-fork-info/hard-fork-info-routing.module'; import { HardForkInfoRoutingModule } from './pages/hard-fork-info/hard-fork-info-routing.module';
import { SettingsModule } from './pages/settings/settings.module'; import { SettingsModule } from './pages/settings/settings.module';
import { TransactionsModule } from './pages/transactions/transactions.module'; import { TransactionsModule } from './pages/transactions/transactions.module';
import { FormsModule } from '@angular/forms';
const routes: Routes = [ const routes: Routes = [
{ {
@ -23,6 +24,7 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forRoot(routes, {}), RouterModule.forRoot(routes, {}),
FormsModule,
HomeRoutingModule, HomeRoutingModule,
DetailRoutingModule, DetailRoutingModule,
TransactionsModule, TransactionsModule,

View file

@ -1,6 +1,6 @@
<app-navbar></app-navbar> <app-navbar></app-navbar>
<div class="d-flex"> <div class="d-flex">
<app-sidebar></app-sidebar> <app-sidebar [isDaemonRunning]="daemonRunning"></app-sidebar>
<div class="col-md-10"> <div class="col-md-10">
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>

View file

@ -2,6 +2,7 @@ import { Component } from '@angular/core';
import { ElectronService } from './core/services'; import { ElectronService } from './core/services';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { APP_CONFIG } from '../environments/environment'; import { APP_CONFIG } from '../environments/environment';
import { DaemonService } from './core/services/daemon/daemon.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -9,9 +10,13 @@ import { APP_CONFIG } from '../environments/environment';
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent { export class AppComponent {
public loading: boolean;
public daemonRunning: boolean;
constructor( constructor(
private electronService: ElectronService, private electronService: ElectronService,
private translate: TranslateService private translate: TranslateService,
private daemonService: DaemonService
) { ) {
this.translate.setDefaultLang('en'); this.translate.setDefaultLang('en');
console.log('APP_CONFIG', APP_CONFIG); console.log('APP_CONFIG', APP_CONFIG);
@ -24,5 +29,22 @@ export class AppComponent {
} else { } else {
console.log('Run in browser'); console.log('Run in browser');
} }
this.loading = false;
this.daemonRunning = false;
this.load();
}
private async load(): Promise<void> {
this.loading = true;
try {
this.daemonRunning = await this.daemonService.isRunning();
}
catch(error) {
console.error(error);
}
this.loading = false;
} }
} }

View file

@ -22,6 +22,7 @@ import { MiningModule } from './pages/mining/mining.module';
import { TransactionsModule } from './pages/transactions/transactions.module'; import { TransactionsModule } from './pages/transactions/transactions.module';
import { OutputsModule } from './pages/outputs/outputs.module'; import { OutputsModule } from './pages/outputs/outputs.module';
import { SidebarComponent } from './shared/components'; import { SidebarComponent } from './shared/components';
import { SettingsModule } from './pages/settings/settings.module';
// AoT requires an exported function for factories // AoT requires an exported function for factories
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json'); const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
@ -40,6 +41,7 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
MiningModule, MiningModule,
TransactionsModule, TransactionsModule,
OutputsModule, OutputsModule,
SettingsModule,
TranslateModule, TranslateModule,
AppRoutingModule, AppRoutingModule,
TranslateModule.forRoot({ TranslateModule.forRoot({

View file

@ -7,8 +7,8 @@ import {
SubmitBlockRequest, GenerateBlocksRequest, GetLastBlockHeaderRequest, SubmitBlockRequest, GenerateBlocksRequest, GetLastBlockHeaderRequest,
GetBlockHeaderByHashRequest, GetBlockHeaderByHeightRequest, GetBlockHeadersRangeRequest, GetBlockHeaderByHashRequest, GetBlockHeaderByHeightRequest, GetBlockHeadersRangeRequest,
GetConnectionsRequest, GetInfoRequest, HardForkInfoRequest, SetBansRequest, GetBansRequest, GetConnectionsRequest, GetInfoRequest, HardForkInfoRequest, SetBansRequest, GetBansRequest,
BannedRequest, FlushTxPoolRequest, GetOutputHistogramRequest, BannedRequest, FlushTxPoolRequest, GetOutputHistogramRequest, GetCoinbaseTxSumRequest,
SyncInfoRequest, SyncInfoRequest, GetOutsRequest,
GetVersionRequest, GetVersionRequest,
GetFeeEstimateRequest, GetFeeEstimateRequest,
GetAlternateChainsRequest, GetAlternateChainsRequest,
@ -17,7 +17,28 @@ import {
CalculatePoWHashRequest, CalculatePoWHashRequest,
FlushCacheRequest, FlushCacheRequest,
GetMinerDataRequest, GetMinerDataRequest,
EmptyRpcRequest EmptyRpcRequest, RPCRequest,
AddAuxPoWRequest,
GetOutputDistributionRequest,
GetBlockRequest,
UpdateRequest,
PopBlocksRequest,
GetTransactionPoolHashesRequest,
GetTransactionPoolHashesBinaryRequest,
GetPublicNodesRequest,
GetNetStatsRequest,
InPeersRequest,
OutPeersRequest,
SetLimitRequest,
StopDaemonRequest,
MiningStatusRequest,
StopMiningRequest,
StartMiningRequest,
SendRawTransactionRequest,
IsKeyImageSpentRequest,
GetAltBlockHashesRequest,
SaveBcRequest,
SetBootstrapDaemonRequest
} from '../../../../common/request'; } from '../../../../common/request';
import { BlockTemplate } from '../../../../common/BlockTemplate'; import { BlockTemplate } from '../../../../common/BlockTemplate';
import { GeneratedBlocks } from '../../../../common/GeneratedBlocks'; import { GeneratedBlocks } from '../../../../common/GeneratedBlocks';
@ -37,14 +58,28 @@ import { BlockchainPruneInfo } from '../../../../common/BlockchainPruneInfo';
import { MinerData } from '../../../../common/MinerData'; import { MinerData } from '../../../../common/MinerData';
import { CoreIsBusyError } from '../../../../common/error'; import { CoreIsBusyError } from '../../../../common/error';
import { ElectronService } from '../electron/electron.service'; import { ElectronService } from '../electron/electron.service';
import { AddedAuxPow } from '../../../../common/AddedAuxPow';
import { AuxPoW } from '../../../../common/AuxPoW';
import { OutputDistribution } from '../../../../common/OutputDistribution';
import { CoinbaseTxSum } from '../../../../common/CoinbaseTxSum';
import { Block } from '../../../../common/Block';
import { Output } from '../../../../common/Output';
import { OutKey } from '../../../../common/OutKey';
import { UpdateInfo } from '../../../../common/UpdateInfo';
import { PublicNode } from '../../../../common/PublicNode';
import { NetStats } from '../../../../common/NetStats';
import { MiningStatus } from '../../../../common/MiningStatus';
import { TxInfo } from '../../../../common/TxInfo';
import { DaemonSettings } from '../../../../common/DaemonSettings';
import { MethodNotFoundError } from '../../../../common/error/MethodNotFoundError';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class DaemonService { export class DaemonService {
private daemonRunning?: boolean; private daemonRunning?: boolean;
private readonly configFilePath: string = './config';
private url: string = "http://127.0.0.1:28081"; private url: string = "http://127.0.0.1:28081";
public settings: DaemonSettings;
//private url: string = "http://node2.monerodevs.org:28089"; //private url: string = "http://node2.monerodevs.org:28089";
//private url: string = "https://testnet.xmr.ditatompel.com"; //private url: string = "https://testnet.xmr.ditatompel.com";
//private url: string = "https://xmr.yemekyedim.com:18081"; //private url: string = "https://xmr.yemekyedim.com:18081";
@ -55,10 +90,66 @@ export class DaemonService {
"Access-Control-Allow-Methods": 'POST,GET' // this states the allowed methods "Access-Control-Allow-Methods": 'POST,GET' // this states the allowed methods
}; };
constructor(private httpClient: HttpClient, private electronService: ElectronService) { } constructor(private httpClient: HttpClient, private electronService: ElectronService) {
this.settings = this.loadSettings();
}
private async callJsonRpc(params: JsonRPCRequest): Promise<{ [key: string]: any }> { private loadSettings(): DaemonSettings {
return await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${this.url}/json_rpc`, params.toDictionary(), this.headers)); /*
const args = [
'--testnet',
'--fast-block-sync', '1',
'--prune-blockchain',
'--sync-pruned-blocks',
'--confirm-external-bind',
'--max-concurrency', '1',
'--log-level', '1',
'--rpc-access-control-origins=*'
];
*/
const settings = new DaemonSettings();
settings.testnet = true;
settings.fastBlockSync = true;
settings.pruneBlockchain = true;
settings.syncPrunedBlocks = true;
settings.confirmExternalBind = true;
settings.logLevel = 1;
settings.rpcAccessControlOrigins = "*";
return settings;
}
private raiseRpcError(error: { code: number, message: string }): void {
if (error.code == -9) {
throw new CoreIsBusyError();
}
else if (error.code == -32601) {
throw new MethodNotFoundError();
}
else
{
throw new Error(error.message);
}
}
private async callRpc(request: RPCRequest): Promise<{ [key: string]: any }> {
let method: string = '';
if (request instanceof JsonRPCRequest) {
method = 'json_rpc';
}
else {
method = request.method;
}
const response = await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${this.url}/${method}`, request.toDictionary(), this.headers));
if (response.error) {
this.raiseRpcError(response.error);
}
return response;
} }
public async startDaemon(): Promise<void> { public async startDaemon(): Promise<void> {
@ -74,7 +165,7 @@ export class DaemonService {
console.log("Starting daemon"); console.log("Starting daemon");
this.electronService.ipcRenderer.send('start-monerod', this.configFilePath); this.electronService.ipcRenderer.send('start-monerod', this.settings.toCommandOptions());
console.log("Daemon started"); console.log("Daemon started");
@ -88,39 +179,52 @@ export class DaemonService {
return this.daemonRunning; return this.daemonRunning;
} }
const response = await this.callJsonRpc(new EmptyRpcRequest()); await this.callRpc(new EmptyRpcRequest());
console.log(response);
this.daemonRunning = true;
} }
catch(error) { catch(error) {
if (error instanceof MethodNotFoundError) {
this.daemonRunning = true;
return this.daemonRunning;
}
console.error(error); console.error(error);
this.daemonRunning = false;
} }
this.daemonRunning = false;
return this.daemonRunning; return this.daemonRunning;
} }
public async getBlock(heightOrHash: number | string, fillPowHash: boolean = false): Promise<Block> {
const response = await this.callRpc(new GetBlockRequest(heightOrHash, fillPowHash));
if (response.error) {
this.raiseRpcError(response.error);
}
return Block.parse(response.result);
}
public async getBlockCount(): Promise<BlockCount> { public async getBlockCount(): Promise<BlockCount> {
const response = await this.callJsonRpc(new GetBlockCountRequest()); const response = await this.callRpc(new GetBlockCountRequest());
return BlockCount.parse(response.result); return BlockCount.parse(response.result);
} }
public async getBlockHash(blockHeight: number): Promise<string> { public async getBlockHash(blockHeight: number): Promise<string> {
const response = await this.callJsonRpc(new GetBlockHashRequest(blockHeight)); const response = await this.callRpc(new GetBlockHashRequest(blockHeight));
return response.result; return response.result;
} }
public async getBlockTemplate(walletAddress: string, reserveSize: number) { public async getBlockTemplate(walletAddress: string, reserveSize: number) {
const response = await this.callJsonRpc(new GetBlockTemplateRequest(walletAddress, reserveSize)); const response = await this.callRpc(new GetBlockTemplateRequest(walletAddress, reserveSize));
return BlockTemplate.parse(response.result); return BlockTemplate.parse(response.result);
} }
public async submitBlock(... blockBlobData: string[]): Promise<void> { public async submitBlock(... blockBlobData: string[]): Promise<void> {
const response = await this.callJsonRpc(new SubmitBlockRequest(blockBlobData)); const response = await this.callRpc(new SubmitBlockRequest(blockBlobData));
if (response.error) { if (response.error) {
if (!response.message) { if (!response.message) {
@ -132,31 +236,31 @@ export class DaemonService {
} }
public async generateBlocks(amountOfBlocks: number, walletAddress: string, prevBlock: string = '', startingNonce: number): Promise<GeneratedBlocks> { public async generateBlocks(amountOfBlocks: number, walletAddress: string, prevBlock: string = '', startingNonce: number): Promise<GeneratedBlocks> {
const response = await this.callJsonRpc(new GenerateBlocksRequest(amountOfBlocks, walletAddress, prevBlock, startingNonce)); const response = await this.callRpc(new GenerateBlocksRequest(amountOfBlocks, walletAddress, prevBlock, startingNonce));
return GeneratedBlocks.parse(response.result); return GeneratedBlocks.parse(response.result);
} }
public async getLastBlockHeader(fillPowHash: boolean = false): Promise<BlockHeader> { public async getLastBlockHeader(fillPowHash: boolean = false): Promise<BlockHeader> {
const response = await this.callJsonRpc(new GetLastBlockHeaderRequest(fillPowHash)); const response = await this.callRpc(new GetLastBlockHeaderRequest(fillPowHash));
return BlockHeader.parse(response.block_header); return BlockHeader.parse(response.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.callJsonRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash)); const response = await this.callRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash));
return BlockHeader.parse(response.block_header); return BlockHeader.parse(response.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.callJsonRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash)); const response = await this.callRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash));
return BlockHeader.parse(response.block_header); return BlockHeader.parse(response.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[]> {
const response = await this.callJsonRpc(new GetBlockHeadersRangeRequest(startHeight, endHeight, fillPowHash)); const response = await this.callRpc(new GetBlockHeadersRangeRequest(startHeight, endHeight, fillPowHash));
const block_headers: any[] = response.block_headers; const block_headers: any[] = response.block_headers;
const result: BlockHeader[] = []; const result: BlockHeader[] = [];
@ -166,7 +270,7 @@ export class DaemonService {
} }
public async getConnections(): Promise<Connection[]> { public async getConnections(): Promise<Connection[]> {
const response = await this.callJsonRpc(new GetConnectionsRequest()); const response = await this.callRpc(new GetConnectionsRequest());
const connections: any[] = response.connections; const connections: any[] = response.connections;
const result: Connection[] = []; const result: Connection[] = [];
@ -176,19 +280,19 @@ export class DaemonService {
} }
public async getInfo(): Promise<DaemonInfo> { public async getInfo(): Promise<DaemonInfo> {
const response = await this.callJsonRpc(new GetInfoRequest()); const response = await this.callRpc(new GetInfoRequest());
return DaemonInfo.parse(response.result); return DaemonInfo.parse(response.result);
} }
public async hardForkInfo(): Promise<HardForkInfo> { public async hardForkInfo(): Promise<HardForkInfo> {
const response = await this.callJsonRpc(new HardForkInfoRequest()); const response = await this.callRpc(new HardForkInfoRequest());
return HardForkInfo.parse(response.result); return HardForkInfo.parse(response.result);
} }
public async setBans(...bans: Ban[]) { public async setBans(...bans: Ban[]) {
const response = await this.callJsonRpc(new SetBansRequest(bans)); const response = await this.callRpc(new SetBansRequest(bans));
if (response.status != 'OK') { if (response.status != 'OK') {
throw new Error(`Error code: ${response.status}`); throw new Error(`Error code: ${response.status}`);
@ -196,7 +300,7 @@ export class DaemonService {
} }
public async getBans(): Promise<Ban[]> { public async getBans(): Promise<Ban[]> {
const response = await this.callJsonRpc(new GetBansRequest()); const response = await this.callRpc(new GetBansRequest());
if (response.error) { if (response.error) {
this.raiseRpcError(response.error); this.raiseRpcError(response.error);
@ -211,7 +315,7 @@ export class DaemonService {
} }
public async banned(address: string): Promise<Ban> { public async banned(address: string): Promise<Ban> {
const response = await this.callJsonRpc(new BannedRequest(address)); const response = await this.callRpc(new BannedRequest(address));
const result = response.result; const result = response.result;
if (result.status != 'OK') { if (result.status != 'OK') {
@ -222,43 +326,78 @@ export class DaemonService {
} }
public async flushTxPool(... txIds: string[]): Promise<void> { public async flushTxPool(... txIds: string[]): Promise<void> {
const response = await this.callJsonRpc(new FlushTxPoolRequest(txIds)); const response = await this.callRpc(new FlushTxPoolRequest(txIds));
if (response.status != 'OK') { if (response.status != 'OK') {
throw new Error(`Error code: ${response.status}`); throw new Error(`Error code: ${response.status}`);
} }
} }
public async getOuts(outputs: Output[], getTxId: boolean): Promise<OutKey[]> {
const response = await this.callRpc(new GetOutsRequest(outputs, getTxId));
if (response.error) {
this.raiseRpcError(response.error);
}
const _outkeys: any[] | undefined = response.outs;
const outkeys: OutKey[] = [];
if (_outkeys) _outkeys.forEach((outkey) => outkeys.push(OutKey.parse(outkey)));
return outkeys;
}
public async getOutputHistogram(amounts: number[], minCount: number, maxCount: number, unlocked: boolean, recentCutoff: number): Promise<HistogramEntry[]> { 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 response = await this.callRpc(new GetOutputHistogramRequest(amounts, minCount, maxCount, unlocked, recentCutoff));
const entries: any[] = response.histogram;
if (response.error) {
this.raiseRpcError(response.error);
}
const entries: any[] = response.result.histogram;
const result: HistogramEntry[] = []; const result: HistogramEntry[] = [];
entries.forEach((entry: any) => result.push(HistogramEntry.parse(entry))); if (entries) entries.forEach((entry: any) => result.push(HistogramEntry.parse(entry)));
return result; return result;
} }
public async getOutputDistribution(amounts: number[], cumulative: boolean, fromHeight: number, toHeight: number): Promise<OutputDistribution[]> {
const response = await this.callRpc(new GetOutputDistributionRequest(amounts, cumulative, fromHeight, toHeight));
if (response.error) {
this.raiseRpcError(response.error);
}
const entries: any[] = response.result.distributions;
const distributions: OutputDistribution[] = [];
if (entries) entries.forEach((entry) => distributions.push(OutputDistribution.parse(entry)));
return distributions;
}
public async syncInfo(): Promise<SyncInfo> { public async syncInfo(): Promise<SyncInfo> {
const response = await this.callJsonRpc(new SyncInfoRequest()); const response = await this.callRpc(new SyncInfoRequest());
return SyncInfo.parse(response.result); return SyncInfo.parse(response.result);
} }
public async getVersion(): Promise<DaemonVersion> { public async getVersion(): Promise<DaemonVersion> {
const response = await this.callJsonRpc(new GetVersionRequest()); const response = await this.callRpc(new GetVersionRequest());
return DaemonVersion.parse(response.result); return DaemonVersion.parse(response.result);
} }
public async getFeeEstimate(): Promise<FeeEstimate> { public async getFeeEstimate(): Promise<FeeEstimate> {
const response = await this.callJsonRpc(new GetFeeEstimateRequest()); const response = await this.callRpc(new GetFeeEstimateRequest());
return FeeEstimate.parse(response.result); return FeeEstimate.parse(response.result);
} }
public async getAlternateChains(): Promise<Chain[]> { public async getAlternateChains(): Promise<Chain[]> {
const response = await this.callJsonRpc(new GetAlternateChainsRequest()); const response = await this.callRpc(new GetAlternateChainsRequest());
const chains: any[] = response.result.chains ? response.result.chains : []; const chains: any[] = response.result.chains ? response.result.chains : [];
const result: Chain[] = []; const result: Chain[] = [];
@ -267,8 +406,18 @@ export class DaemonService {
return result; return result;
} }
public async getCoinbaseTxSum(height: number, count: number): Promise<CoinbaseTxSum> {
const response = await this.callRpc(new GetCoinbaseTxSumRequest(height, count));
if (response.error) {
this.raiseRpcError(response.error);
}
return CoinbaseTxSum.parse(response.result);
}
public async relayTx(... txIds: string[]): Promise<void> { public async relayTx(... txIds: string[]): Promise<void> {
const response = await this.callJsonRpc(new RelayTxRequest(txIds)); const response = await this.callRpc(new RelayTxRequest(txIds));
if (response.result.status != 'OK') { if (response.result.status != 'OK') {
throw new Error(`Error code: ${response.result.status}`); throw new Error(`Error code: ${response.result.status}`);
@ -276,25 +425,25 @@ export class DaemonService {
} }
public async getTxPoolBacklog(): Promise<TxBacklogEntry[]> { public async getTxPoolBacklog(): Promise<TxBacklogEntry[]> {
const response = await this.callJsonRpc(new GetTxPoolBacklogRequest()); const response = await this.callRpc(new GetTxPoolBacklogRequest());
return TxBacklogEntry.fromBinary(response.backlog); return TxBacklogEntry.fromBinary(response.backlog);
} }
public async pruneBlockchain(check: boolean = false): Promise<BlockchainPruneInfo> { public async pruneBlockchain(check: boolean = false): Promise<BlockchainPruneInfo> {
const response = await this.callJsonRpc(new PruneBlockchainRequest(check)); const response = await this.callRpc(new PruneBlockchainRequest(check));
return BlockchainPruneInfo.parse(response.result); return BlockchainPruneInfo.parse(response.result);
} }
public async calculatePoWHash(majorVersion: number, height: number, blockBlob: string, seedHash: string): Promise<string> { public async calculatePoWHash(majorVersion: number, height: number, blockBlob: string, seedHash: string): Promise<string> {
const response = await this.callJsonRpc(new CalculatePoWHashRequest(majorVersion, height, blockBlob, seedHash)); const response = await this.callRpc(new CalculatePoWHashRequest(majorVersion, height, blockBlob, seedHash));
return response.result; return response.result;
} }
public async flushCache(badTxs: boolean = false, badBlocks: boolean = false): Promise<void> { public async flushCache(badTxs: boolean = false, badBlocks: boolean = false): Promise<void> {
const response = await this.callJsonRpc(new FlushCacheRequest(badTxs, badBlocks)); const response = await this.callRpc(new FlushCacheRequest(badTxs, badBlocks));
if(response.result.status != 'OK') { if(response.result.status != 'OK') {
throw new Error(`Error code: ${response.result.status}`); throw new Error(`Error code: ${response.result.status}`);
@ -302,25 +451,151 @@ export class DaemonService {
} }
public async getMinerData(): Promise<MinerData> { public async getMinerData(): Promise<MinerData> {
const response = await this.callJsonRpc(new GetMinerDataRequest()); const response = await this.callRpc(new GetMinerDataRequest());
if (response.error) {
this.raiseRpcError(response.error);
}
return MinerData.parse(response.result); return MinerData.parse(response.result);
} }
private raiseRpcError(error: { code: number, message: string }): void { public async AddAuxPoW(blockTemplateBlob: string, auxPoW: AuxPoW[]): Promise<AddedAuxPow> {
const response = await this.callRpc(new AddAuxPoWRequest(blockTemplateBlob, auxPoW));
if (error.code == -9) { return AddedAuxPow.parse(response.result);
throw new CoreIsBusyError(); }
}
else
{
throw new Error(error.message);
}
public async setBootstrapDaemon(address: string, username: string = '', password: string = '', proxy: string = ''): Promise<void> {
const response = await this.callRpc(new SetBootstrapDaemonRequest(address, username, password, proxy));
if (typeof response.status == 'string' && response.status != 'OK') {
throw new Error(`Could not set bootstrap daemon: ${response.status}`);
}
}
public async saveBc(): Promise<void> {
const response = await this.callRpc(new SaveBcRequest());
if (typeof response.status == 'string' && response.status != 'OK') {
throw new Error(`Could not save blockchain: ${response.status}`);
}
}
public async getAltBlockHashes(): Promise<string[]> {
const response = await this.callRpc(new GetAltBlockHashesRequest());
return response.blks_hashes;
}
public async isKeyImageSpent(...keyImages: string[]): Promise<number[]> {
const response = await this.callRpc(new IsKeyImageSpentRequest(keyImages));
return response.spent_status;
}
public async sendRawTransaction(txAsHex: string, doNotRelay: boolean = false): Promise<TxInfo> {
const response = await this.callRpc(new SendRawTransactionRequest(txAsHex, doNotRelay));
return TxInfo.parse(response);
}
public async startMining(doBackgroundMining: boolean, ignoreBattery: boolean, minerAddress: string, threadsCount: number): Promise<void> {
const response = await this.callRpc(new StartMiningRequest(doBackgroundMining, ignoreBattery, minerAddress, threadsCount));
if (typeof response.status == 'string' && response.status != 'OK') {
throw new Error(`Could not start mining: ${response.status}`);
}
}
public async stopMining(): Promise<void> {
const response = await this.callRpc(new StopMiningRequest());
if (typeof response.status == 'string' && response.status != 'OK') {
throw new Error(`Could not stop mining: ${response.status}`);
}
}
public async miningStatus(): Promise<MiningStatus> {
const response = await this.callRpc(new MiningStatusRequest());
return MiningStatus.parse(response);
}
public async stopDaemon(): Promise<void> {
const response = await this.callRpc(new StopDaemonRequest());
if (typeof response.status == 'string' && response.status != 'OK') {
throw new Error(`Could not stop daemon: ${response.status}`);
}
}
public async setLimit(limitDown: number, limitUp: number): Promise<{ limitDown: number, limitUp: number }> {
const response = await this.callRpc(new SetLimitRequest(limitDown, limitUp));
return {
limitDown: response.limit_down,
limitUp: response.limit_up
};
}
public async inPeers(inPeers: number): Promise<number> {
const response = await this.callRpc(new InPeersRequest(inPeers));
return response.in_peers;
}
public async outPeers(outPeers: number): Promise<number> {
const response = await this.callRpc(new OutPeersRequest(outPeers));
return response.out_peers;
}
public async getNetStats(): Promise<NetStats> {
const response = await this.callRpc(new GetNetStatsRequest());
return NetStats.parse(response);
}
public async getPublicNodes(whites: boolean = true, grays: boolean = false, includeBlocked: boolean = false): Promise<PublicNode[]> {
const response = await this.callRpc(new GetPublicNodesRequest(whites, grays, includeBlocked));
const _whites: any[] | undefined = response.whites;
const _grays: any[] | undefined = response.grays;
const nodes: PublicNode[] = [];
if (_whites) _whites.forEach((white) => nodes.push(PublicNode.parse(white, 'white')));
if (_grays) _grays.forEach((gray) => nodes.push(PublicNode.parse(gray, 'gray')));
return nodes;
}
public async getTransactionPoolHashes(): Promise<string[]> {
const response = await this.callRpc(new GetTransactionPoolHashesRequest());
return response.tx_hashes;
}
public async getTransactionPoolHashesBinary(): Promise<string> {
const response = await this.callRpc(new GetTransactionPoolHashesBinaryRequest());
return response.tx_hashes;
}
public async popBlocks(nBlocks: number): Promise<number> {
const response = await this.callRpc(new PopBlocksRequest(nBlocks));
return response.height;
}
public async update(command: 'check' | 'download', path: string = ''): Promise<UpdateInfo> {
const response = await this.callRpc(new UpdateRequest(command, path));
return UpdateInfo.parse(response);
}
public async checkUpdate(): Promise<UpdateInfo> {
return await this.update('check');
}
public async downloadUpdate(path: string = ''): Promise<UpdateInfo> {
return await this.update('download', path);
} }
} }

View file

@ -4,7 +4,7 @@
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" tabindex="0"> <div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" tabindex="0">
<div class="row d-flex"> <div class="row d-flex">
<div *ngIf="!daemonRunning" class="h-100 p-5 text-bg-dark rounded-3 m-4"> <div *ngIf="!daemonRunning" class="h-100 p-5 text-bg-dark rounded-3 m-4 text-center">
<h2><i class="bi bi-exclamation-diamond"></i> Daemon not running</h2> <h2><i class="bi bi-exclamation-diamond"></i> Daemon not running</h2>
<p>Start monero daemon</p> <p>Start monero daemon</p>
<button *ngIf="!startingDaemon" class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button> <button *ngIf="!startingDaemon" class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button>

View file

@ -90,7 +90,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
console.log('DetailComponent AFTER VIEW INIT'); console.log('DetailComponent AFTER VIEW INIT');
this.navbarService.setNavbarLinks(this.navbarLinks); this.navbarService.setNavbarLinks(this.navbarLinks);
this.loadInterval = setInterval(() => { setTimeout(() => {
this.ngZone.run(() => { this.ngZone.run(() => {
if (this.isLoading) { if (this.isLoading) {
return; return;
@ -103,7 +103,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
this.load(); this.load();
}); });
}, 5000); }, 500);
} }
ngOnDestroy(): void { ngOnDestroy(): void {

View file

@ -50,7 +50,8 @@ export class MiningComponent implements AfterViewInit {
new NavbarLink('pills-block-template-tab', '#pills-block-template', 'block-template', false, 'Block Template'), new NavbarLink('pills-block-template-tab', '#pills-block-template', 'block-template', false, 'Block Template'),
new NavbarLink('pills-generate-blocks-tab', '#pills-generate-blocks', 'generate-blocks', false, 'Generate Blocks'), new NavbarLink('pills-generate-blocks-tab', '#pills-generate-blocks', 'generate-blocks', false, 'Generate Blocks'),
new NavbarLink('pills-submit-block-tab', '#pills-submit-block', 'submit-block', false, 'Submit Block'), new NavbarLink('pills-submit-block-tab', '#pills-submit-block', 'submit-block', false, 'Submit Block'),
new NavbarLink('pills-calc-pow-tab', '#pills-calc-pow', 'calc-pow', false, 'Calculate PoW Hash') new NavbarLink('pills-calc-pow-tab', '#pills-calc-pow', 'calc-pow', false, 'Calculate PoW Hash'),
new NavbarLink('pills-add-aux-pow-tab', '#pills-add-aux-pow', 'add-aux-pow', false, 'Add Aux PoW')
]; ];
this.router.events.subscribe((event) => { this.router.events.subscribe((event) => {

View file

@ -7,73 +7,73 @@
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="offline" class="form-check-label">Offline</label> <label for="offline" class="form-check-label">Offline</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="offline"> <input class="form-control form-check-input" type="checkbox" role="switch" id="offline" (change)="OnOfflineChange()">
<br> <br>
<small class="text-body-secondary">Do not listen for peers, nor connect to any</small> <small class="text-body-secondary">Do not listen for peers, nor connect to any</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="public-node" class="form-check-label">Public node</label> <label for="public-node" class="form-check-label">Public node</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="public-node"> <input class="form-control form-check-input" type="checkbox" role="switch" id="public-node" (change)="OnPublicNodeChange()">
<br> <br>
<small class="text-body-secondary">Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P</small> <small class="text-body-secondary">Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="restricted-rpc" class="form-check-label">Restricted RPC</label> <label for="restricted-rpc" class="form-check-label">Restricted RPC</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="restricted-rpc"> <input class="form-control form-check-input" type="checkbox" role="switch" id="restricted-rpc" (change)="OnRestrictedRPCChange()">
<br> <br>
<small class="text-body-secondary">Restrict RPC to view-only commands and do not return privacy sensitive data in RPC calls</small> <small class="text-body-secondary">Restrict RPC to view-only commands and do not return privacy sensitive data in RPC calls</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="confirm-external-bind" class="form-check-label">Confirm external bind</label> <label for="confirm-external-bind" class="form-check-label">Confirm external bind</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="confirm-external-bind"> <input class="form-control form-check-input" type="checkbox" role="switch" id="confirm-external-bind" (change)="OnConfirmExternalBindChange()">
<br> <br>
<small class="text-body-secondary">Confirm Bind IP is not a loopback (local) IP</small> <small class="text-body-secondary">Confirm Bind IP is not a loopback (local) IP</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="rpc-ignore-ipv4" class="form-check-label">Ignore IPv4</label> <label for="rpc-ignore-ipv4" class="form-check-label">Ignore IPv4</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ignore-ipv4"> <input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ignore-ipv4" (change)="OnIgnoreIPv4Change()">
<br> <br>
<small class="text-body-secondary">Ignore unsuccessful IPv4 bind for RPC</small> <small class="text-body-secondary">Ignore unsuccessful IPv4 bind for RPC</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="disable-rpc-ban" class="form-check-label">Disable ban</label> <label for="disable-rpc-ban" class="form-check-label">Disable ban</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="disable-rpc-ban"> <input class="form-control form-check-input" type="checkbox" role="switch" id="disable-rpc-ban" (change)="OnDisableRpcBanChange()">
<br> <br>
<small class="text-body-secondary">Do not ban hosts on RPC errors</small> <small class="text-body-secondary">Do not ban hosts on RPC errors</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="rpc-bind-ip" class="form-label">Bind IP</label> <label for="rpc-bind-ip" class="form-label">Bind IP</label>
<input type="text" class="form-control" id="rpc-bind-ip" placeholder="127.0.0.1"> <input type="text" class="form-control" id="rpc-bind-ip" placeholder="127.0.0.1" [(ngModel)]="currentSettings.rpcBindIp" [ngModelOptions]="{standalone: true}" >
<small class="text-body-secondary">Specify IP to bind RPC server</small> <small class="text-body-secondary">Specify IP to bind RPC server</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="rpc-bind-port" class="form-label">Bind port</label> <label for="rpc-bind-port" class="form-label">Bind port</label>
<input type="number" class="form-control" id="rpc-bind-port" placeholder="18081"> <input type="number" class="form-control" id="rpc-bind-port" placeholder="18081" [(ngModel)]="currentSettings.rpcBindPort" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">18081 for mainnet, 28081 for testnet, 38081 for stagenet</small> <small class="text-body-secondary">18081 for mainnet, 28081 for testnet, 38081 for stagenet</small>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="rpc-login-user" class="form-label">Username</label> <label for="rpc-login-user" class="form-label">Username</label>
<input type="text" class="form-control" id="rpc-login-user" placeholder="" value=""> <input type="text" class="form-control" id="rpc-login-user" placeholder="" [(ngModel)]="rpcLoginUser" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify username required for RPC server</small> <small class="text-body-secondary">Specify username required for RPC server</small>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="rpc-login-password" class="form-label">Password</label> <label for="rpc-login-password" class="form-label">Password</label>
<input type="password" class="form-control" id="rpc-login-password" placeholder="" value=""> <input type="password" class="form-control" id="rpc-login-password" placeholder="" [(ngModel)]="rpcLoginPassword" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify password required for RPC server</small> <small class="text-body-secondary">Specify password required for RPC server</small>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<label for="rpc-access-control-origins" class="form-label">Access control origins</label> <label for="rpc-access-control-origins" class="form-label">Access control origins</label>
<input type="text" class="form-control" id="rpc-access-control-origins" placeholder="" value=""> <input type="text" class="form-control" id="rpc-access-control-origins" placeholder="" [(ngModel)]="currentSettings.rpcAccessControlOrigins" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify a comma separated list of origins to allow cross origin resource sharing</small> <small class="text-body-secondary">Specify a comma separated list of origins to allow cross origin resource sharing</small>
</div> </div>
</div> </div>
@ -85,14 +85,14 @@
<div class="form-check form-switch col-md-12"> <div class="form-check form-switch col-md-12">
<label for="rpc-use-ipv6" class="form-check-label">Enabled</label> <label for="rpc-use-ipv6" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-use-ipv6"> <input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-use-ipv6" [(ngModel)]="currentSettings.rpcUseIpv6" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Allow IPv6 for RPC</small> <small class="text-body-secondary">Allow IPv6 for RPC</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="rpc-bind-ipv6-address" class="form-label">Bind IPv6 address</label> <label for="rpc-bind-ipv6-address" class="form-label">Bind IPv6 address</label>
<input type="text" class="form-control" id="rpc-bind-ipv6-address" placeholder="::1"> <input type="text" class="form-control" id="rpc-bind-ipv6-address" placeholder="::1" [(ngModel)]="currentSettings.rpcBindIpv6Address" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify IPv6 address to bind RPC server</small> <small class="text-body-secondary">Specify IPv6 address to bind RPC server</small>
</div> </div>
@ -111,26 +111,26 @@
<div class="form-check form-switch col-md-12"> <div class="form-check form-switch col-md-12">
<label for="enable-zmq" class="form-check-label">Enabled</label> <label for="enable-zmq" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="enable-zmq"> <input class="form-control form-check-input" type="checkbox" role="switch" id="enable-zmq" [checked]="!currentSettings.noZmq" (change)="OnNoZmqChange()">
<br> <br>
<small class="text-body-secondary">Enable ZMQ RPC Server</small> <small class="text-body-secondary">Enable ZMQ RPC Server</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="zmq-rpc-bind-ip" class="form-label">Bind IP</label> <label for="zmq-rpc-bind-ip" class="form-label">Bind IP</label>
<input type="text" class="form-control" id="zmq-rpc-bind-ip" placeholder="127.0.0.1"> <input type="text" class="form-control" id="zmq-rpc-bind-ip" placeholder="127.0.0.1" [(ngModel)]="currentSettings.zmqRpcBindIp" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">IP for ZMQ RPC Server to listen on</small> <small class="text-body-secondary">IP for ZMQ RPC Server to listen on</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="zmq-rpc-bind-port" class="form-label">Bind port</label> <label for="zmq-rpc-bind-port" class="form-label">Bind port</label>
<input type="number" class="form-control" id="zmq-rpc-bind-port" placeholder="18082"> <input type="number" class="form-control" id="zmq-rpc-bind-port" placeholder="18082" [(ngModel)]="currentSettings.zmqRpcBindPort" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">18082 for mainnet, 28082 for testnet, 38082 for stagenet</small> <small class="text-body-secondary">18082 for mainnet, 28082 for testnet, 38082 for stagenet</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="zmq-pub" class="form-label">ZMQ Pub</label> <label for="zmq-pub" class="form-label">ZMQ Pub</label>
<input type="text" class="form-control" id="zmq-pub" placeholder="tcp://ip:port or ipc://path"> <input type="text" class="form-control" id="zmq-pub" placeholder="tcp://ip:port or ipc://path" [(ngModel)]="currentSettings.zmqPub" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Address for ZMQ Pub</small> <small class="text-body-secondary">Address for ZMQ Pub</small>
</div> </div>
@ -143,25 +143,25 @@
<div class="col-md-12"> <div class="col-md-12">
<label for="rpc-payment-address" class="form-label">Address</label> <label for="rpc-payment-address" class="form-label">Address</label>
<input type="text" class="form-control" id="rpc-payment-address"> <input type="text" class="form-control" id="rpc-payment-address" [(ngModel)]="currentSettings.rpcPaymentAddress" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Restrict RPC to clients sending micropayment to this address</small> <small class="text-body-secondary">Restrict RPC to clients sending micropayment to this address</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="rpc-payment-difficulty" class="form-label">Difficulty</label> <label for="rpc-payment-difficulty" class="form-label">Difficulty</label>
<input type="number" class="form-control" id="rpc-payment-difficulty" placeholder="1000"> <input type="number" class="form-control" id="rpc-payment-difficulty" placeholder="1000" [(ngModel)]="currentSettings.rpcPaymentDifficuly" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Restrict RPC to clients sending micropayment at this difficulty</small> <small class="text-body-secondary">Restrict RPC to clients sending micropayment at this difficulty</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="rpc-payment-credits" class="form-label">Credits</label> <label for="rpc-payment-credits" class="form-label">Credits</label>
<input type="number" class="form-control" id="rpc-payment-credits" placeholder="100"> <input type="number" class="form-control" id="rpc-payment-credits" placeholder="100" [(ngModel)]="currentSettings.rpcPaymentCredits" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Restrict RPC to clients sending micropayment, yelds that many credits per payment</small> <small class="text-body-secondary">Restrict RPC to clients sending micropayment, yelds that many credits per payment</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="rpc-payment-allow-free-loopback" class="form-check-label">Allow free loopback</label> <label for="rpc-payment-allow-free-loopback" class="form-check-label">Allow free loopback</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-payment-allow-free-loopback"> <input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-payment-allow-free-loopback" [(ngModel)]="currentSettings.rpcPaymentAllowFreeLoopback" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Allow free access from the loopback address (ie, the local host)</small> <small class="text-body-secondary">Allow free access from the loopback address (ie, the local host)</small>
</div> </div>
@ -175,43 +175,43 @@
<div class="col-md-4"> <div class="col-md-4">
<label for="rpc-ssl" class="form-label">SSL Mode</label> <label for="rpc-ssl" class="form-label">SSL Mode</label>
<select class="form-select" id="rpc-ssl"> <select class="form-select" id="rpc-ssl" [(ngModel)]="currentSettings.rpcSsl" [ngModelOptions]="{standalone: true}">
<option value="autodetect">Autodetect</option> <option [ngValue]="'autodetect'">Autodetect</option>
<option value="enabled">Enabled</option> <option [ngValue]="'enabled'">Enabled</option>
<option value="disabled">Disabled</option> <option [ngValue]="'disabled'">Disabled</option>
</select> </select>
</div> </div>
<div class="form-check form-switch col-md-4"> <div class="form-check form-switch col-md-4">
<label for="rpc-ssl-allow-chained" class="form-check-label">Allow chained</label> <label for="rpc-ssl-allow-chained" class="form-check-label">Allow chained</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ssl-allow-chained"> <input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ssl-allow-chained" [(ngModel)]="currentSettings.rpcSslAllowChained" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Allow user chain certificates</small> <small class="text-body-secondary">Allow user chain certificates</small>
</div> </div>
<div class="form-check form-switch col-md-4"> <div class="form-check form-switch col-md-4">
<label for="rpc-ssl-allow-any-cert" class="form-check-label">Allow any cert</label> <label for="rpc-ssl-allow-any-cert" class="form-check-label">Allow any cert</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ssl-allow-any-cert"> <input class="form-control form-check-input" type="checkbox" role="switch" id="rpc-ssl-allow-any-cert" [(ngModel)]="currentSettings.rpcSslAllowAnyCert" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Allow any peer certificate</small> <small class="text-body-secondary">Allow any peer certificate</small>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<label for="rpc-ssl-private-key" class="form-label">Private key</label> <label for="rpc-ssl-private-key" class="form-label">Private key</label>
<input type="file" class="form-control" id="rpc-ssl-private-key"> <input type="file" class="form-control" id="rpc-ssl-private-key" [(ngModel)]="currentSettings.rpcSslPrivateKey" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Path to a PEM format private key</small> <small class="text-body-secondary">Path to a PEM format private key</small>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<label for="rpc-ssl-certificate" class="form-label">Certificate</label> <label for="rpc-ssl-certificate" class="form-label">Certificate</label>
<input type="file" class="form-control" id="rpc-ssl-certificate"> <input type="file" class="form-control" id="rpc-ssl-certificate" [(ngModel)]="currentSettings.rpcSslCertificate" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Path to a PEM format certificate</small> <small class="text-body-secondary">Path to a PEM format certificate</small>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<label for="rpc-ssl-ca-certificates" class="form-label">CA Certificates</label> <label for="rpc-ssl-ca-certificates" class="form-label">CA Certificates</label>
<input type="file" class="form-control" id="rpc-ssl-ca-certificates"> <input type="file" class="form-control" id="rpc-ssl-ca-certificates" [(ngModel)]="currentSettings.rpcSslCACertificates" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Path to file containing concatenated PEM format certificate(s) to replace system CA(s)</small> <small class="text-body-secondary">Path to file containing concatenated PEM format certificate(s) to replace system CA(s)</small>
</div> </div>
@ -229,33 +229,33 @@
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="allow-local-ip" class="form-check-label">Allow local IP</label> <label for="allow-local-ip" class="form-check-label">Allow local IP</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="allow-local-ip"> <input class="form-control form-check-input" type="checkbox" role="switch" id="allow-local-ip" [(ngModel)]="currentSettings.allowLocalIp" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Allow local ip add to peer list, mostly in debug process</small> <small class="text-body-secondary">Allow local ip add to peer list, mostly in debug process</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="p2p-ignore-ipv4" class="form-check-label">Ignore IPv4</label> <label for="p2p-ignore-ipv4" class="form-check-label">Ignore IPv4</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="p2p-ignore-ipv4"> <input class="form-control form-check-input" type="checkbox" role="switch" id="p2p-ignore-ipv4" [(ngModel)]="currentSettings.p2pIgnoreIpv4">
<br> <br>
<small class="text-body-secondary">Ignore unsuccessful IPv4 bind for P2P</small> <small class="text-body-secondary">Ignore unsuccessful IPv4 bind for P2P</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="p2p-bind-ip" class="form-label">Bind IP</label> <label for="p2p-bind-ip" class="form-label">Bind IP</label>
<input type="text" class="form-control" id="p2p-bind-ip" placeholder="0.0.0.0"> <input type="text" class="form-control" id="p2p-bind-ip" placeholder="0.0.0.0" [(ngModel)]="currentSettings.p2pBindIp" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Interface for p2p network protocol</small> <small class="text-body-secondary">Interface for p2p network protocol</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="p2p-bind-port" class="form-label">Bind port</label> <label for="p2p-bind-port" class="form-label">Bind port</label>
<input type="number" class="form-control" id="p2p-bind-port" placeholder="18080"> <input type="number" class="form-control" id="p2p-bind-port" placeholder="18080" [(ngModel)]="currentSettings.p2pBindPort" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">18080 for mainnet, 28080 for testnet, 38080 for stagenet</small> <small class="text-body-secondary">18080 for mainnet, 28080 for testnet, 38080 for stagenet</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="p2p-external-port" class="form-label">External port</label> <label for="p2p-external-port" class="form-label">External port</label>
<input type="number" class="form-control" id="p2p-external-port" placeholder="18080"> <input type="number" class="form-control" id="p2p-external-port" placeholder="18080" [(ngModel)]="currentSettings.p2pExternalPort" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">External port for p2p network protocol (if port forwarding used with NAT)</small> <small class="text-body-secondary">External port for p2p network protocol (if port forwarding used with NAT)</small>
</div> </div>
@ -266,13 +266,13 @@
<div class="col-md-6"> <div class="col-md-6">
<label for="p2p-bind-ipv6-address" class="form-label">Bind IPv6 address</label> <label for="p2p-bind-ipv6-address" class="form-label">Bind IPv6 address</label>
<input type="text" class="form-control" id="p2p-bind-ipv6-address" placeholder="::1"> <input type="text" class="form-control" id="p2p-bind-ipv6-address" placeholder="::1" [(ngModel)]="currentSettings.p2pBindIpv6Address" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify IPv6 address to bind RPC server</small> <small class="text-body-secondary">Specify IPv6 address to bind RPC server</small>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="p2p-bind-port-ipv6" class="form-label">Bind IPv6 port</label> <label for="p2p-bind-port-ipv6" class="form-label">Bind IPv6 port</label>
<input type="number" class="form-control" id="p2p-bind-port-ipv6" placeholder="18080"> <input type="number" class="form-control" id="p2p-bind-port-ipv6" placeholder="18080" [(ngModel)]="currentSettings.p2pBindPortIpv6" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">18080 for mainnet, 28080 for testnet, 38080 for stagenet</small> <small class="text-body-secondary">18080 for mainnet, 28080 for testnet, 38080 for stagenet</small>
</div> </div>
@ -293,13 +293,13 @@
<div class="col-12"> <div class="col-12">
<label for="address" class="form-label">Address</label> <label for="address" class="form-label">Address</label>
<input type="text" class="form-control" id="address" placeholder="Use 'auto' to enable automatic discovering and switching"> <input type="text" class="form-control" id="address" placeholder="Use 'auto' to enable automatic discovering and switching" [(ngModel)]="currentSettings.bootstrapDaemonAddress" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">URL of a bootstrap remote daemon that the connected wallets can use while this daemon is still not fully synced.</small> <small class="text-body-secondary">URL of a bootstrap remote daemon that the connected wallets can use while this daemon is still not fully synced.</small>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="firstName" class="form-label">Username</label> <label for="firstName" class="form-label">Username</label>
<input type="text" class="form-control" id="firstName" placeholder="" value=""> <input type="text" class="form-control" id="firstName" placeholder="">
<small class="text-body-secondary">Specify username for the bootstrap daemon login</small> <small class="text-body-secondary">Specify username for the bootstrap daemon login</small>
</div> </div>
@ -312,7 +312,7 @@
<div class="col-12"> <div class="col-12">
<label for="bootstrap-daemon-proxy" class="form-label">Proxy</label> <label for="bootstrap-daemon-proxy" class="form-label">Proxy</label>
<input type="text" class="form-control" id="bootstrap-daemon-proxy" placeholder="ip:port"> <input type="text" class="form-control" id="bootstrap-daemon-proxy" placeholder="ip:port" [(ngModel)]="currentSettings.bootstrapDaemonProxy" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Socks proxy to use for bootstrap daemon connection</small> <small class="text-body-secondary">Socks proxy to use for bootstrap daemon connection</small>
</div> </div>
@ -325,59 +325,59 @@
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="sync-enabled" class="form-check-label">Enabled</label> <label for="sync-enabled" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="sync-enabled" checked> <input class="form-control form-check-input" type="checkbox" role="switch" id="sync-enabled" [checked]="!currentSettings.noSync" (change)="OnSyncEnableChange()">
<br> <br>
<small class="text-body-secondary">Synchronize the blockchain with other peers</small> <small class="text-body-secondary">Synchronize the blockchain with other peers</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="prune-blockchain" class="form-check-label">Prune Blockchain</label> <label for="prune-blockchain" class="form-check-label">Prune Blockchain</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="prune-blockchain"> <input class="form-control form-check-input" type="checkbox" role="switch" id="prune-blockchain" [(ngModel)]="currentSettings.pruneBlockchain" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Reduce blockchain disk usage</small> <small class="text-body-secondary">Reduce blockchain disk usage</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="sync-pruned-blocks" class="form-check-label">Sync pruned blocks</label> <label for="sync-pruned-blocks" class="form-check-label">Sync pruned blocks</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="sync-pruned-blocks"> <input class="form-control form-check-input" type="checkbox" role="switch" id="sync-pruned-blocks" [(ngModel)]="currentSettings.syncPrunedBlocks" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Allow syncing from nodes with only pruned blocks</small> <small class="text-body-secondary">Allow syncing from nodes with only pruned blocks</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="fast-block-sync" class="form-check-label">Fast block sync</label> <label for="fast-block-sync" class="form-check-label">Fast block sync</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="fast-block-sync"> <input class="form-control form-check-input" type="checkbox" role="switch" id="fast-block-sync" [(ngModel)]="currentSettings.fastBlockSync" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Sync up most of the way by using embedded, known block hashes</small> <small class="text-body-secondary">Sync up most of the way by using embedded, known block hashes</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="relay-fluffly-blocks" class="form-check-label">Relay fluffy blocks</label> <label for="relay-fluffly-blocks" class="form-check-label">Relay fluffy blocks</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="relay-fluffly-blocks" checked> <input class="form-control form-check-input" type="checkbox" role="switch" id="relay-fluffly-blocks" [checked]="!currentSettings.noFluffyBlocks" (change)="OnRelayFlufflyBlocksChange()">
<br> <br>
<small class="text-body-secondary">Relay blocks as fluffy blocks</small> <small class="text-body-secondary">Relay blocks as fluffy blocks</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="keep-alt-blocks" class="form-check-label">Keep alternative blocks</label> <label for="keep-alt-blocks" class="form-check-label">Keep alternative blocks</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="keep-alt-blocks"> <input class="form-control form-check-input" type="checkbox" role="switch" id="keep-alt-blocks" [(ngModel)]="currentSettings.keepAltBlocks" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Keep alternative blocks on restart</small> <small class="text-body-secondary">Keep alternative blocks on restart</small>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="block-sync-size" class="form-label">Block sync size</label> <label for="block-sync-size" class="form-label">Block sync size</label>
<input type="number" class="form-control" id="block-sync-size" placeholder="" value="0"> <input type="number" class="form-control" id="block-sync-size" placeholder="" [(ngModel)]="currentSettings.blockSyncSize" [ngModelOptions]="{standalone: true}">
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="block-download-max-size" class="form-label">Block download max size</label> <label for="block-download-max-size" class="form-label">Block download max size</label>
<input type="number" class="form-control" id="block-download-max-size" placeholder="" value="0"> <input type="number" class="form-control" id="block-download-max-size" placeholder="" [(ngModel)]="currentSettings.blockDownloadMaxSize" [ngModelOptions]="{standalone: true}">
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="db-sync-mode" class="form-label">Database sync mode</label> <label for="db-sync-mode" class="form-label">Database sync mode</label>
<input type="text" class="form-control" id="db-sync-mode" placeholder="fast:async:250000000bytes"> <input type="text" class="form-control" id="db-sync-mode" placeholder="fast:async:250000000bytes" [(ngModel)]="currentSettings.dbSyncMode" [ngModelOptions]="{standalone: true}">
</div> </div>
<hr class="my-4"> <hr class="my-4">
@ -386,59 +386,20 @@
<div class="my-3"> <div class="my-3">
<div class="form-check"> <div class="form-check">
<input id="credit" name="paymentMethod" type="radio" class="form-check-input" checked="" required=""> <input id="credit" name="paymentMethod" type="radio" class="form-check-input">
<label class="form-check-label" for="credit">mainnet</label> <label class="form-check-label" for="credit">mainnet</label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input id="debit" name="paymentMethod" type="radio" class="form-check-input" required=""> <input id="debit" name="paymentMethod" type="radio" class="form-check-input" [(ngModel)]="currentSettings.testnet" [ngModelOptions]="{standalone: true}">
<label class="form-check-label" for="debit">testnet</label> <label class="form-check-label" for="debit">testnet</label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input id="paypal" name="paymentMethod" type="radio" class="form-check-input" required=""> <input id="paypal" name="paymentMethod" type="radio" class="form-check-input" [(ngModel)]="currentSettings.stagenet" [ngModelOptions]="{standalone: true}">
<label class="form-check-label" for="paypal">stagenet</label> <label class="form-check-label" for="paypal">stagenet</label>
</div> </div>
</div> </div>
</div> </div>
<!--
<div class="row gy-3">
<div class="col-md-6">
<label for="cc-name" class="form-label">Name on card</label>
<input type="text" class="form-control" id="cc-name" placeholder="" required="">
<small class="text-body-secondary">Full name as displayed on card</small>
<div class="invalid-feedback">
Name on card is required
</div>
</div>
<div class="col-md-6">
<label for="cc-number" class="form-label">Credit card number</label>
<input type="text" class="form-control" id="cc-number" placeholder="" required="">
<div class="invalid-feedback">
Credit card number is required
</div>
</div>
<div class="col-md-3">
<label for="cc-expiration" class="form-label">Expiration</label>
<input type="text" class="form-control" id="cc-expiration" placeholder="" required="">
<div class="invalid-feedback">
Expiration date required
</div>
</div>
<div class="col-md-3">
<label for="cc-cvv" class="form-label">CVV</label>
<input type="text" class="form-control" id="cc-cvv" placeholder="" required="">
<div class="invalid-feedback">
Security code required
</div>
</div>
</div>
-->
<hr class="my-4">
</form> </form>
</div> </div>
</div> </div>
@ -454,33 +415,33 @@
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="bg-mining-enable" class="form-check-label">Enabled</label> <label for="bg-mining-enable" class="form-check-label">Enabled</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="bg-mining-enable"> <input class="form-control form-check-input" type="checkbox" role="switch" id="bg-mining-enable" [(ngModel)]="currentSettings.bgMiningEnable" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Enable background mining</small> <small class="text-body-secondary">Enable background mining</small>
</div> </div>
<div class="form-check form-switch col-md-6"> <div class="form-check form-switch col-md-6">
<label for="bg-mining-ignore-battery" class="form-check-label">Ignore battery</label> <label for="bg-mining-ignore-battery" class="form-check-label">Ignore battery</label>
<input class="form-control form-check-input" type="checkbox" role="switch" id="bg-mining-ignore-battery"> <input class="form-control form-check-input" type="checkbox" role="switch" id="bg-mining-ignore-battery" [(ngModel)]="currentSettings.bgMiningIgnoreBattery" [ngModelOptions]="{standalone: true}">
<br> <br>
<small class="text-body-secondary">Reduce blockchain disk usage</small> <small class="text-body-secondary">Reduce blockchain disk usage</small>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="bg-mining-min-idle-interval" class="form-label">Minimum idle interval</label> <label for="bg-mining-min-idle-interval" class="form-label">Minimum idle interval</label>
<input type="number" class="form-control" id="bg-mining-min-idle-interval" placeholder="" value=""> <input type="number" class="form-control" id="bg-mining-min-idle-interval" placeholder="" [(ngModel)]="currentSettings.bgMiningMinIdleInterval" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify min lookback interval in seconds for determining idle state</small> <small class="text-body-secondary">Specify min lookback interval in seconds for determining idle state</small>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="bg-mining-idle-threshold" class="form-label">Idle threshold</label> <label for="bg-mining-idle-threshold" class="form-label">Idle threshold</label>
<input type="number" class="form-control" id="bg-mining-idle-threshold" placeholder="" value=""> <input type="number" class="form-control" id="bg-mining-idle-threshold" placeholder="" [(ngModel)]="currentSettings.bgMiningIdleThreshold" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify minimum avg idle percentage over lookback interval</small> <small class="text-body-secondary">Specify minimum avg idle percentage over lookback interval</small>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="bg-mining-miner-target" class="form-label">Miner target</label> <label for="bg-mining-miner-target" class="form-label">Miner target</label>
<input type="number" class="form-control" id="bg-mining-miner-target" placeholder=""> <input type="number" class="form-control" id="bg-mining-miner-target" placeholder="" [(ngModel)]="currentSettings.bgMiningMinerTarget" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify maximum percentage cpu use by miners</small> <small class="text-body-secondary">Specify maximum percentage cpu use by miners</small>
</div> </div>
@ -501,24 +462,24 @@
<div class="col-md-4"> <div class="col-md-4">
<label for="log-level" class="form-label">Log level</label> <label for="log-level" class="form-label">Log level</label>
<select class="form-select" id="log-level"> <select class="form-select" id="log-level" [(ngModel)]="currentSettings.logLevel" [ngModelOptions]="{standalone: true}">
<option value="0">Info</option> <option [ngValue]="0">Info</option>
<option value="1">Warning</option> <option [ngValue]="1">Warning</option>
<option value="2">Debug</option> <option [ngValue]="2">Debug</option>
<option value="3">Error</option> <option [ngValue]="3">Error</option>
<option value="4">Trace</option> <option [ngValue]="4">Trace</option>
</select> </select>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="max-log-file-size" class="form-label">Max log file size</label> <label for="max-log-file-size" class="form-label">Max log file size</label>
<input type="number" class="form-control" id="max-log-file-size" placeholder="104850000" value=""> <input type="number" class="form-control" id="max-log-file-size" placeholder="104850000" [(ngModel)]="currentSettings.maxLogFileSize" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify maximum log file size [B]</small> <small class="text-body-secondary">Specify maximum log file size [B]</small>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="max-log-files" class="form-label">Max log files</label> <label for="max-log-files" class="form-label">Max log files</label>
<input type="number" class="form-control" id="max-log-files" placeholder="50" value=""> <input type="number" class="form-control" id="max-log-files" placeholder="50" [(ngModel)]="currentSettings.maxLogFiles" [ngModelOptions]="{standalone: true}">
<small class="text-body-secondary">Specify maximum number of rotated log files to be saved (no limit by setting to 0)</small> <small class="text-body-secondary">Specify maximum number of rotated log files to be saved (no limit by setting to 0)</small>
</div> </div>
</div> </div>
@ -530,6 +491,6 @@
<hr class="my-4"> <hr class="my-4">
<button class="w-50 btn btn-primary btn-lg" type="submit">Save</button> <button class="w-50 btn btn-primary btn-lg" type="submit" [disabled]="!modified" (click)="OnSave()">Save</button>
</div> </div>

View file

@ -2,17 +2,27 @@ import { AfterViewInit, Component } from '@angular/core';
import { NavbarService } from '../../shared/components/navbar/navbar.service'; import { NavbarService } from '../../shared/components/navbar/navbar.service';
import { NavigationEnd, NavigationStart, Router } from '@angular/router'; import { NavigationEnd, NavigationStart, Router } from '@angular/router';
import { NavbarLink } from '../../shared/components/navbar/navbar.model'; import { NavbarLink } from '../../shared/components/navbar/navbar.model';
import { DaemonSettings } from '../../../common/DaemonSettings';
import { FormsModule, NgModel } from '@angular/forms';
@Component({ @Component({
selector: 'app-settings', selector: 'app-settings',
templateUrl: './settings.component.html', templateUrl: './settings.component.html',
styleUrl: './settings.component.scss' styleUrl: './settings.component.scss',
imports: [FormsModule],
standalone: true
}) })
export class SettingsComponent implements AfterViewInit { export class SettingsComponent implements AfterViewInit {
private readonly navbarLinks: NavbarLink[]; private readonly navbarLinks: NavbarLink[];
private originalSettings: DaemonSettings;
public currentSettings: DaemonSettings;
public rpcLoginUser: string;
public rpcLoginPassword: string;
constructor(private router: Router, private navbarService: NavbarService) { constructor(private router: Router, private navbarService: NavbarService) {
this.navbarLinks = [ this.navbarLinks = [
new NavbarLink('pills-rpc-tab', '#pills-rpc', 'pills-rpc', true, 'RPC'), new NavbarLink('pills-rpc-tab', '#pills-rpc', 'pills-rpc', true, 'RPC'),
@ -21,6 +31,18 @@ export class SettingsComponent implements AfterViewInit {
new NavbarLink('pills-mining-tab', '#pills-mining', 'pills-mining', false, 'Mining'), new NavbarLink('pills-mining-tab', '#pills-mining', 'pills-mining', false, 'Mining'),
new NavbarLink('pills-logs-tab', '#pills-logs', 'pills-logs', false, 'Logs') new NavbarLink('pills-logs-tab', '#pills-logs', 'pills-logs', false, 'Logs')
]; ];
this.originalSettings = new DaemonSettings();
this.currentSettings = this.originalSettings.clone();
const loginArgs = this.currentSettings.rpcLogin.split(":");
if (loginArgs.length == 2) {
this.rpcLoginPassword = loginArgs[0];
this.rpcLoginUser = loginArgs[1];
}
else {
this.rpcLoginUser = '';
this.rpcLoginPassword = '';
}
this.router.events.subscribe((event) => { this.router.events.subscribe((event) => {
if (event instanceof NavigationEnd) { if (event instanceof NavigationEnd) {
@ -30,13 +52,66 @@ export class SettingsComponent implements AfterViewInit {
}); });
} }
public get modified(): boolean {
if (!this.currentSettings.equals(this.originalSettings)) {
return true;
}
return false;
}
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.navbarService.setNavbarLinks(this.navbarLinks); this.navbarService.setNavbarLinks(this.navbarLinks);
} }
public OnOfflineChange() {
this.currentSettings.offline = !this.currentSettings.offline;
}
public OnPublicNodeChange() {
this.currentSettings.publicNode = !this.currentSettings.publicNode;
}
public OnRestrictedRPCChange() {
this.currentSettings.restrictedRpc = !this.currentSettings.restrictedRpc;
}
public OnConfirmExternalBindChange() {
this.currentSettings.confirmExternalBind = !this.currentSettings.confirmExternalBind;
}
public OnIgnoreIPv4Change() {
this.currentSettings.rpcIgnoreIpv4 = !this.currentSettings.rpcIgnoreIpv4;
}
public OnDisableRpcBanChange() {
this.currentSettings.disableRpcBan = !this.currentSettings.disableRpcBan;
}
public OnRpcUseIPv6Change(): void {
this.currentSettings.rpcUseIpv6 = !this.currentSettings.rpcUseIpv6;
}
public OnNoZmqChange(): void {
this.currentSettings.noZmq = !this.currentSettings.noZmq;
}
public OnSyncEnableChange(): void {
this.currentSettings.noSync != this.currentSettings.noSync;
}
public OnRelayFlufflyBlocksChange(): void {
this.currentSettings.noFluffyBlocks = !this.currentSettings.noFluffyBlocks;
}
private onNavigationEnd(): void { private onNavigationEnd(): void {
} }
public OnSave(): void {
}
} }
/** /**
* --log-file arg (=/home/sidney/.bitmonero/bitmonero.log, /home/sidney/.bitmonero/testnet/bitmonero.log if 'testnet', /home/sidney/.bitmonero/stagenet/bitmonero.log if 'stagenet') * --log-file arg (=/home/sidney/.bitmonero/bitmonero.log, /home/sidney/.bitmonero/testnet/bitmonero.log if 'testnet', /home/sidney/.bitmonero/stagenet/bitmonero.log if 'stagenet')

View file

@ -2,12 +2,16 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { SettingsRoutingModule } from './settings-routing.module'; import { SettingsRoutingModule } from './settings-routing.module';
import { SharedModule } from '../../shared/shared.module';
import { FormsModule } from '@angular/forms';
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [ imports: [
CommonModule, CommonModule,
FormsModule,
SharedModule,
SettingsRoutingModule SettingsRoutingModule
] ]
}) })

View file

@ -1 +1,22 @@
<p>transactions works!</p> <div class="tab-pane fade show active" id="pills-rpc" role="tabpanel" aria-labelledby="pills-rpc-tab" tabindex="0">
<div class="row g-5 m-2">
<div class="col-md-7 col-lg-10">
<h4 class="mb-3">Relay a list of transaction IDs</h4>
<form class="needs-validation" novalidate="">
<div class="row g-3">
<div class="col-12">
<label for="tx_ids" class="form-label">Tx Ids</label>
<textarea type="text" class="form-control" id="tx_ids" placeholder="" rows="15" cols="15"></textarea>
<small class="text-body-secondary">List of transaction IDs to relay</small>
</div>
<hr class="my-4">
</div>
</form>
</div>
</div>
<button class="w-100 btn btn-primary btn-lg" type="button" [disabled]="!canRelay" (click)="onRelay()">Relay Tx</button>
</div>

View file

@ -11,6 +11,8 @@ import { NavbarLink } from '../../shared/components/navbar/navbar.model';
export class TransactionsComponent implements AfterViewInit { export class TransactionsComponent implements AfterViewInit {
private readonly navbarLinks: NavbarLink[]; private readonly navbarLinks: NavbarLink[];
public canRelay: boolean;
constructor(private daemonService: DaemonService, private navbarService: NavbarService) { constructor(private daemonService: DaemonService, private navbarService: NavbarService) {
this.navbarLinks = [ this.navbarLinks = [
new NavbarLink('pills-relay-tx-tab', '#pills-relay-tx', 'pills-relay-tx', true, 'Relay Tx', true), new NavbarLink('pills-relay-tx-tab', '#pills-relay-tx', 'pills-relay-tx', true, 'Relay Tx', true),
@ -18,6 +20,8 @@ export class TransactionsComponent implements AfterViewInit {
new NavbarLink('pills-flush-tx-pool-tab', '#pills-flush-tx-pool', 'pills-flush-tx-pool', false, 'Flush Tx Pool', true), new NavbarLink('pills-flush-tx-pool-tab', '#pills-flush-tx-pool', 'pills-flush-tx-pool', false, 'Flush Tx Pool', true),
new NavbarLink('pills-flush-cahe', '#pills-flush-cache', 'pills-flush-cache', false, 'Flush Cache', true) new NavbarLink('pills-flush-cahe', '#pills-flush-cache', 'pills-flush-cache', false, 'Flush Cache', true)
]; ];
this.canRelay = false;
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
@ -27,4 +31,8 @@ export class TransactionsComponent implements AfterViewInit {
private async load(): Promise<void> { private async load(): Promise<void> {
} }
public async onRelay(): Promise<void> {
}
} }

View file

@ -1,5 +1,5 @@
import { CommonModule, NgClass, NgFor } from '@angular/common'; import { CommonModule, NgClass, NgFor } from '@angular/common';
import { Component } from '@angular/core'; import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { ChildActivationEnd, ChildActivationStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterEvent, RouterModule, RoutesRecognized } from '@angular/router'; import { ChildActivationEnd, ChildActivationStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterEvent, RouterModule, RoutesRecognized } from '@angular/router';
@Component({ @Component({
@ -7,22 +7,32 @@ import { ChildActivationEnd, ChildActivationStart, NavigationCancel, NavigationE
templateUrl: './sidebar.component.html', templateUrl: './sidebar.component.html',
styleUrl: './sidebar.component.scss' styleUrl: './sidebar.component.scss'
}) })
export class SidebarComponent { export class SidebarComponent implements OnChanges {
public readonly navLinks: NavLink[]; @Input() public isDaemonRunning: boolean = false;
public navLinks: NavLink[];
public isLoading: boolean; public isLoading: boolean;
public errorMessage: string; public errorMessage: string;
constructor(private router: Router) { constructor(private router: Router) {
this.navLinks = [ if (!this.isDaemonRunning) {
new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'), this.navLinks = [
new NavLink('Blockchain', '/blockchain', 'bi bi-bounding-box'), new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
new NavLink('Transactions', '/transactions', 'bi bi-credit-card-2-front'), new NavLink('Settings', '/settings', 'bi bi-gear')
new NavLink('Outputs', '/outputs', 'bi bi-circle-fill'), ];
new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'), }
new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'), else {
new NavLink('Bans', '/bans', 'bi bi-ban'), this.navLinks = [
new NavLink('Settings', '/settings', 'bi bi-gear') new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
]; new NavLink('Blockchain', '/blockchain', 'bi bi-bounding-box'),
new NavLink('Transactions', '/transactions', 'bi bi-credit-card-2-front'),
new NavLink('Outputs', '/outputs', 'bi bi-circle-fill'),
new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'),
new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'),
new NavLink('Bans', '/bans', 'bi bi-ban'),
new NavLink('Settings', '/settings', 'bi bi-gear')
];
}
this.isLoading = false; this.isLoading = false;
this.errorMessage = ''; this.errorMessage = '';
} }
@ -31,6 +41,27 @@ export class SidebarComponent {
return navLink.path == this.router.url; return navLink.path == this.router.url;
} }
public ngOnChanges(changes: SimpleChanges): void {
if (!this.isDaemonRunning) {
this.navLinks = [
new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
new NavLink('Settings', '/settings', 'bi bi-gear')
];
}
else {
this.navLinks = [
new NavLink('Dashboard', '/detail', 'bi bi-speedometer2'),
new NavLink('Blockchain', '/blockchain', 'bi bi-bounding-box'),
new NavLink('Transactions', '/transactions', 'bi bi-credit-card-2-front'),
new NavLink('Outputs', '/outputs', 'bi bi-circle-fill'),
new NavLink('Mining', '/mining', 'bi bi-minecart-loaded'),
new NavLink('Hard Fork Info', '/hardforkinfo', 'bi bi-signpost-split'),
new NavLink('Bans', '/bans', 'bi bi-ban'),
new NavLink('Settings', '/settings', 'bi bi-gear')
];
}
}
} }
class NavLink { class NavLink {

31
src/common/AddedAuxPow.ts Normal file
View file

@ -0,0 +1,31 @@
import { AuxPoW } from "./AuxPoW";
export class AddedAuxPow {
public readonly blockTemplateBlob: string;
public readonly blockHashingBlob: string;
public readonly merkleRoot: string;
public readonly merkleTreeDepth: string;
public auxPoW: AuxPoW[];
constructor(blockTemplateBlob: string, blockHashingBlob: string, merkleRoot: string, merkleTreeDepth: string, auxPoW: AuxPoW[]) {
this.blockTemplateBlob = blockTemplateBlob;
this.blockHashingBlob = blockHashingBlob;
this.merkleRoot = merkleRoot;
this.merkleTreeDepth = merkleTreeDepth;
this.auxPoW = auxPoW;
}
public static parse(addedAuxPow: any): AddedAuxPow {
const blockTemplateBlob: string = addedAuxPow.blocktemplate_blob;
const blockHashingBlob: string = addedAuxPow.blockhashing_blob;
const merkleRoot: string = addedAuxPow.merkle_root;
const merkleTreeDepth: string = addedAuxPow.merkle_tree_depth;
const auxPoW: AuxPoW[] = [];
const _auxPoW: any[] | undefined = addedAuxPow.aux_pow;
if (_auxPoW) _auxPoW.forEach((_pow) => auxPoW.push(AuxPoW.parse(_pow)));
return new AddedAuxPow(blockTemplateBlob, blockHashingBlob, merkleRoot, merkleTreeDepth, auxPoW);
}
}

20
src/common/AuxPoW.ts Normal file
View file

@ -0,0 +1,20 @@
export class AuxPoW {
public readonly id: string;
public readonly hash: string;
constructor(id: string, hash: string) {
this.id = id;
this.hash = hash;
}
public toDictionary(): { [key: string]: any } {
return {
'id': this.id,
'hash': this.hash
}
}
public static parse(auxPoW: any): AuxPoW {
return new AuxPoW(auxPoW.id, auxPoW.hash);
}
}

22
src/common/Block.ts Normal file
View file

@ -0,0 +1,22 @@
import { BlockDetails } from "./BlockDetails";
import { BlockHeader } from "./BlockHeader";
export class Block {
public readonly blob: string;
public readonly blockHeader: BlockHeader;
public readonly details: BlockDetails;
constructor(blob: string, blockHeader: BlockHeader, details: BlockDetails) {
this.blob = blob;
this.blockHeader = blockHeader;
this.details = details;
}
public static parse(block: any): Block {
const blob = block.blob;
const blockHeader = BlockHeader.parse(block.block_header);
const details = BlockDetails.parse(block.json);
return new Block(blob, blockHeader, details);
}
}

View file

@ -0,0 +1,34 @@
import { MinerTx } from "./MinerTx";
export class BlockDetails {
public majorVersion: number;
public minorVersion: number;
public timestamp: number;
public prevId: string;
public nonce: number;
public minerTx: MinerTx;
public txHashes: string[];
constructor(majorVersion: number, minorVersion: number, timestamp: number, prevId: string, nonce: number, minerTx: MinerTx, txHashes: string[]) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.timestamp = timestamp;
this.prevId = prevId;
this.nonce = nonce;
this.minerTx = minerTx;
this.txHashes = txHashes;
}
public static parse(details: string): BlockDetails {
const blockDetails = JSON.parse(details);
const majorVersion = blockDetails.major_version;
const minorVersion = blockDetails.minor_version;
const timestamp = blockDetails.timestamp;
const prevId = blockDetails.prev_id;
const nonce = blockDetails.nonce;
const minerTx = MinerTx.parse(blockDetails.miner_tx);
const txHashes = blockDetails.tx_hashes;
return new BlockDetails(majorVersion, minorVersion, timestamp, prevId, nonce, minerTx, txHashes);
}
}

View file

@ -0,0 +1,31 @@
export class CoinbaseTxSum {
public readonly emissionAmount: number;
public readonly emissionAmountTop64: number;
public readonly feeAmount: number;
public readonly feeAmountTop64: number;
public readonly topHash: string;
public readonly wideEmissionAmount: string;
public readonly wideFeeAmount: string;
constructor(emissionAmount: number, emissionAmountTop64: number, feeAmount: number, feeAmountTop64: number, topHash: string, wideEmissionAmount: string, wideFeeAmount: string) {
this.emissionAmount = emissionAmount;
this.emissionAmountTop64 = emissionAmountTop64;
this.feeAmount = feeAmount;
this.feeAmountTop64 = feeAmountTop64;
this.topHash = topHash;
this.wideEmissionAmount = wideEmissionAmount;
this.wideFeeAmount = wideFeeAmount;
}
public static parse(coinbaseTxSum: any): CoinbaseTxSum {
const emissionAmount = coinbaseTxSum.emission_amount;
const emissionAmountTop64 = coinbaseTxSum.emission_amount_top64;
const feeAmount = coinbaseTxSum.fee_amount;
const feeAmountTop64 = coinbaseTxSum.fee_amount_top64;
const topHash = coinbaseTxSum.top_hash;
const wideEmissionAmount = coinbaseTxSum.wide_emission_amount;
const wideFeeAmount = coinbaseTxSum.wide_fee_amount;
return new CoinbaseTxSum(emissionAmount, emissionAmountTop64, feeAmount, feeAmountTop64, topHash, wideEmissionAmount, wideFeeAmount);
}
}

View file

@ -128,8 +128,121 @@ export class DaemonSettings {
public rpcPaymentAllowFreeLoopback: boolean = false; public rpcPaymentAllowFreeLoopback: boolean = false;
public disableRpcBan: boolean = false; public disableRpcBan: boolean = false;
public toCommandOptions(): string { public equals(settings: DaemonSettings): boolean {
let options: string = ''; return this.toCommandOptions().join('') == settings.toCommandOptions().join('');
}
public clone(): DaemonSettings {
return Object.assign(new DaemonSettings(), this);
}
public static parse(data: any): DaemonSettings {
const settings = new DaemonSettings();
Object.assign(settings, data);
return settings;
}
public toCommandOptions(): string[] {
const options: string[] = [];
if (this.mainnet) options.push(`--mainnet`);
else if (this.testnet) options.push(`--testnet`);
else if (this.stagenet) options.push(`--stagenet`);
if (this.logFile != '') options.push('--log-file', this.logFile);
if (this.logLevel >= 0 && this.logLevel <= 4) options.push('--log-level', `${this.logLevel}`);
if (this.maxLogFileSize) options.push(`--max-log-file-size=${this.maxLogFileSize}`);
if (this.maxLogFiles) options.push(`--max-log-files=${this.maxLogFiles}`);
if (this.maxConcurrency) options.push(`--max-concurrency=${this.maxConcurrency}`);
if (this.proxy != '') options.push(`--proxy=${this.proxy}`);
if (this.proxyAllowDnsLeaks) options.push(`--proxy-allow-dns-leaks`);
if (this.publicNode) options.push(`--public-node`);
if (this.noZmq) options.push(`--no-zmq`);
if (!this.noZmq && this.zmqRpcBindIp != '') options.push(`--zmq-rpc-bind-ip`, this.zmqRpcBindIp);
if (!this.noZmq && this.zmqRpcBindPort) options.push(`--zmq-rpc-bind-port`, `${this.zmqRpcBindPort}`);
if (!this.noZmq && this.zmqPub != '') options.push(`--zmq-pub`, this.zmqPub);
if (this.testDropDownload) options.push(`--test-drop-download`);
if (this.testDropDownload && this.testDropDownloadHeight) options.push(`--test-drop-download-height`);
if (this.testDbgLockSleep) options.push(`--tet-dbg-lock-sleep`, `${this.testDbgLockSleep}`);
if (this.regtest) options.push(`--regtest`);
if (this.keepFakeChain) options.push(`--keep-fakechain`);
if (this.fixedDifficulty) options.push(`--fixed-difficulty`, `${this.fixedDifficulty}`);
if (this.enforceDnsCheckpoint) options.push(`--enforce-dns-checkpoint`);
if (this.prepBlocksThreads) options.push(`--prep-block-threads`, `${this.prepBlocksThreads}`);
if (this.fastBlockSync) options.push(`--fast-block-sync`, `1`);
if (this.showTimeStats) options.push(`--show-time-stats`);
if (this.blockSyncSize) options.push(`--block-sync-size`, `${this.blockSyncSize}`);
if (this.checkUpdates) options.push(`--check-updates`, this.checkUpdates);
if (this.noFluffyBlocks) options.push(`--no-fluffy-blocks`);
if (this.offline) options.push(`--offline`);
if (this.disableDnsCheckpoints) options.push(`--disable-dns-checkpoints`);
if (this.blockDownloadMaxSize) options.push(`--block-download-max-size`, `${this.blockDownloadMaxSize}`);
if (this.syncPrunedBlocks) options.push(`--sync-pruned-blocks`);
if (this.maxTxPoolWeight) options.push(`--max-txpool-weight`, `${this.maxTxPoolWeight}`);
if (this.blockNotify != '') options.push(`--block-notify`, this.blockNotify);
if (this.pruneBlockchain) options.push('--prune-blockchain');
if (this.reorgNotify != '') options.push(`--reorg-notify`, this.reorgNotify);
if (this.blockRateNotify != '') options.push(`--block-rate-notify`, this.blockRateNotify);
if (this.keepAltBlocks) options.push(`--keep-alt-blocks`);
if (this.extraMessagesFile != '') options.push(`--extra-messages-file`, this.extraMessagesFile);
if (this.startMining != '') options.push(`--start-mining`, this.startMining);
if (this.miningThreds) options.push(`--mining-threads`, `${this.miningThreds}`);
if (this.bgMiningEnable) options.push(`--bg-mining-enable`);
if (this.bgMiningIgnoreBattery) options.push(`--bg-mining-ignore-battery`);
if (this.bgMiningIdleThreshold) options.push(`--bg-mining-idle-threshold`, `${this.bgMiningIdleThreshold}`);
if (this.bgMiningMinIdleInterval) options.push(`--bg-mining-idle-interval`, `${this.bgMiningMinIdleInterval}`);
if (this.bgMiningMinerTarget) options.push(`--bg-mining-miner-target`, `${this.bgMiningMinerTarget}`);
if (this.dbSyncMode != '') options.push(`--db-sync-mode`, `${this.dbSyncMode}`);
if (this.dbSalvage) options.push(`--db-salvage`);
if (this.p2pBindIp != '') options.push(`--p2p-bind-ip`, this.p2pBindIp);
if (this.p2pBindIpv6Address != '') options.push(`--p2p-bind-ipv6-address`, this.p2pBindIpv6Address);
if (this.p2pBindPort) options.push(`--p2p-bind-port`, `${this.p2pBindPort}`);
if (this.p2pBindPortIpv6) options.push(`--p2p-bind-port-ipv6`, `${this.p2pBindPortIpv6}`);
if (this.p2pUseIpv6) options.push(`--p2p-use-ipv6`);
if (this.p2pIgnoreIpv4) options.push(`--p2p-ignore-ipv4`);
if (this.p2pExternalPort) options.push(`--p2p-external-port`, `${this.p2pExternalPort}`);
if (this.allowLocalIp) options.push(`--allow-local-ip`);
if (this.addPeer != '') options.push('--add-peer', this.addPeer);
if (this.addPriorityNode != '') options.push(`--add-priority-node`, this.addPriorityNode);
if (this.addExclusiveNode != '') options.push(`--add-exlcusive-node`, this.addExclusiveNode);
if (this.seedNode != '') options.push(`--seed-node`, this.seedNode);
if (this.txProxy != '') options.push(`--tx-proxy`, this.txProxy);
if (this.anonymousInbound != '') options.push(`--anonymous-inbound`, this.anonymousInbound);
if (this.banList != '') options.push(`--ban-list`, this.banList);
if (this.hideMyPort) options.push(`--hide-my-port`);
if (this.noSync) options.push(`--no-sync`);
if (this.enableDnsBlocklist) options.push(`--enable-dns-block-list`);
if (this.noIgd) options.push(`--no-igd`);
if (this.outPeers >= 0) options.push(`--out-peers`, `${this.outPeers}`);
if (this.inPeers >= 0) options.push(`--in-peers`, `${this.inPeers}`);
if (this.tosFlag >= 0) options.push(`--tos-flag`, `${this.tosFlag}`);
if (this.limitRate >= 0) options.push(`--limit-rate`, `${this.limitRate}`);
if (this.limitRateUp >= 0) options.push(`--limit-rate-up`, `${this.limitRateUp}`);
if (this.limitRateDown >= 0) options.push(`--limit-rate-down`, `${this.limitRateDown}`);
if (this.padTransactions) options.push(`--pad-transactions`);
if (this.maxConnectionsPerIp >= 0) options.push(`--max-connections-per-ip`, `${this.maxConnectionsPerIp}`);
if (this.rpcBindIp != '') options.push(`--rpc-bind-ip`, `${this.rpcBindIp}`);
if (this.rpcBindPort) options.push(`--rpc-bind-ip`, `${this.rpcBindIp}`);
if (this.restrictedBindPort) options.push(`--restricted-bind-port`, `${this.restrictedBindPort}`);
if (this.restrictedRpc) options.push(`--restricted-rpc`);
if (this.bootstrapDaemonAddress != '') options.push(`--bootstrap-daemon-address`, this.bootstrapDaemonAddress);
if (this.bootstrapDaemonLogin != '') options.push(`--bootstrap-daemon-login`, this.bootstrapDaemonLogin);
if (this.bootstrapDaemonProxy != '') options.push(`--bootstrap-daemon-proxy`, this.bootstrapDaemonProxy);
if (this.confirmExternalBind) options.push(`--confirm-external-bind`);
if (this.rpcAccessControlOrigins != '') options.push(`--rpc-access-control-origins=${this.rpcAccessControlOrigins}`);
if (this.rpcSsl) options.push(`--rpc-ssl`, this.rpcSsl);
if (this.rpcSslPrivateKey) options.push(`--rpc-ssl-private-key`, this.rpcSslPrivateKey);
if (this.rpcSslCertificate) options.push(`--rpc-ssl-certificate`, this.rpcSslCertificate);
if (this.rpcSslCACertificates) options.push(`--rpc-ssl-ca-certificates`, this.rpcSslCACertificates);
if (this.rpcAllowedFingerprints) options.push(`--rpc-allowed-fingerprints`, this.rpcAllowedFingerprints);
if (this.rpcSslAllowChained) options.push(`--rpc-ssl-allow-chained`);
if (this.rpcSslAllowAnyCert) options.push(`--rpc-ssl-allow-any-cert`);
if (this.rpcPaymentAddress != '') options.push(`--rpc-payment-address`, this.rpcPaymentAddress);
if (this.rpcPaymentDifficuly) options.push(`--rpc-payment-difficulty`, `${this.rpcPaymentDifficuly}`);
if (this.rpcPaymentCredits) options.push(`--rpc-payment-credits`, `${this.rpcPaymentCredits}`);
if (this.rpcPaymentAllowFreeLoopback) options.push(`--rpc-payment-allow-free-loopback`);
if (this.disableRpcBan) options.push(`--disable-rpc-ban`);
return options; return options;
} }

125
src/common/MinerTx.ts Normal file
View file

@ -0,0 +1,125 @@
export class MinerTx {
public readonly version: number;
public readonly unlockTime: number;
public readonly vin: TxInput[];
public readonly vout: TxOutput[];
public readonly extra: number[];
public readonly rctSignatures: RctSignatures;
constructor(version: number, unlockTime: number, vin: TxInput[], vout: TxOutput[], extra: number[], rctSignatures: RctSignatures) {
this.version = version;
this.unlockTime = unlockTime;
this.vin = vin;
this.vout = vout;
this.extra = extra;
this.rctSignatures = rctSignatures;
}
public static parse(minerTx: any): MinerTx {
const version = minerTx.version;
const unlockTime = minerTx.unlock_time;
const _vin: any[] | undefined = minerTx.vin;
const _vout: any[] | undefined = minerTx.vout;
const extra = minerTx.extra;
const rctSignatures = RctSignatures.parse(minerTx.rct_signatures);
const vin: TxInput[] = [];
const vout: TxOutput[] = [];
if (_vin) _vin.forEach((v) => vin.push(TxInput.parse(v)));
if (_vout) _vout.forEach((v) => vout.push(TxOutput.parse(v)));
return new MinerTx(version, unlockTime, vin, vout, extra, rctSignatures);
}
}
export class TxInput {
public readonly gen: TxInputGen;
constructor(gen: TxInputGen) {
this.gen = gen;
}
public static parse(input: any): TxInput {
const gen = TxInputGen.parse(input.gen);
return new TxInput(gen);
}
}
export class TxInputGen {
public readonly height: number;
constructor(height: number) {
this.height = height;
}
public static parse(gen: any): TxInputGen {
const height = gen.height;
return new TxInputGen(height);
}
}
export class TxOutput {
public readonly amount: number;
public readonly target: TxOutputTarget;
constructor(amount: number, target: TxOutputTarget) {
this.amount = amount;
this.target = target;
}
public static parse(out: any): TxOutput {
const amount = out.amount;
const target = TxOutputTarget.parse(out.target);
return new TxOutput(amount, target);
}
}
export class RctSignatures {
public readonly type: number;
constructor(type: number) {
this.type = type;
}
public static parse(rctSignatures: any): RctSignatures {
const type = rctSignatures.type;
return new RctSignatures(type);
}
}
export class TxOutputTarget {
public readonly taggedKey: TaggedKey;
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;
constructor(key: string, viewTag: string) {
this.key = key;
this.viewTag = viewTag;
}
public static parse(taggedKey: any): TaggedKey {
const key = taggedKey.key;
const viewTag = taggedKey.view_tag;
return new TaggedKey(key, viewTag);
}
}

View file

@ -0,0 +1,91 @@
export class MiningStatus {
public readonly active: boolean;
public readonly address: string;
public readonly bgIdleThreshold: number;
public readonly bgMinIdleSeconds: number;
public readonly bgTarget: number;
public readonly blockReward: number;
public readonly blockTarget: number;
public readonly difficulty: number;
public readonly difficultyTop64: number;
public readonly isBackgroundMiningEnabled: boolean;
public readonly powAlgorithm: string;
public readonly speed: number;
public readonly threadsCount: number;
public readonly wideDifficulty: string;
constructor(
active: boolean,
address: string,
bgIdleThreshold: number,
bgMinIdleSeconds: number,
bgTarget: number,
blockReward: number,
blockTarget: number,
difficulty: number,
difficultyTop64: number,
isBackgroundMiningEnabled: boolean,
powAlgorithm: string,
speed: number,
threadsCount: number,
wideDifficulty: string
) {
this.active = active;
this.address = address;
this.bgIdleThreshold = bgIdleThreshold;
this.bgMinIdleSeconds = bgMinIdleSeconds;
this.bgTarget = bgTarget;
this.blockReward = blockReward;
this.blockTarget = blockTarget;
this.difficulty = difficulty;
this.difficultyTop64 = difficultyTop64;
this.isBackgroundMiningEnabled = isBackgroundMiningEnabled;
this.powAlgorithm = powAlgorithm;
this.speed = speed;
this.threadsCount = threadsCount;
this.wideDifficulty = wideDifficulty;
}
public static parse(miningStatus: any): MiningStatus {
return new MiningStatus(
miningStatus.active,
miningStatus.address,
miningStatus.bg_idle_threshold,
miningStatus.bg_min_idle_seconds,
miningStatus.bg_target,
miningStatus.block_reward,
miningStatus.block_target,
miningStatus.difficulty,
miningStatus.difficulty_top64,
miningStatus.is_background_mining_enabled,
miningStatus.pow_algorithm,
miningStatus.speed,
miningStatus.threads_count,
miningStatus.wide_difficulty
);
}
}
/**
* {
"active": true,
"address": "47xu3gQpF569au9C2ajo5SSMrWji6xnoE5vhr94EzFRaKAGw6hEGFXYAwVADKuRpzsjiU1PtmaVgcjUJF89ghGPhUXkndHc",
"bg_idle_threshold": 0,
"bg_ignore_battery": false,
"bg_min_idle_seconds": 0,
"bg_target": 0,
"block_reward": 1181637918707,
"block_target": 120,
"difficulty": 239928394679,
"difficulty_top64": 0,
"is_background_mining_enabled": false,
"pow_algorithm": "RandomX",
"speed": 23,
"status": "OK",
"threads_count": 1,
"untrusted": false,
"wide_difficulty": "0x37dcd8c3b7"
}
*/

34
src/common/NetStats.ts Normal file
View file

@ -0,0 +1,34 @@
export class NetStats {
public readonly startTime: number;
public readonly totalPacketsIn: number;
public readonly totalBytesIn: number;
public readonly totalBytesOut: number;
constructor(startTime: number, totalPacketsIn: number, totalBytesIn: number, totalBytesOut: number) {
this.startTime = startTime;
this.totalPacketsIn = totalPacketsIn;
this.totalBytesIn = totalBytesIn;
this.totalBytesOut = totalBytesOut;
}
public static parse(netStats: any): NetStats {
const startTime = netStats.start_time;
const totalPacketsIn = netStats.total_packets_in;
const totalBytesIn = netStats.total_bytes_in;
const totalBytesOut = netStats.total_bytes_out;
return new NetStats(startTime, totalPacketsIn, totalBytesIn, totalBytesOut);
}
}
/**
* start_time - unsigned int; Unix start time.
total_packets_in - unsigned int;
total_bytes_in - unsigned int;
total_packets_out - unsigned int;
total_bytes_out - unsigned int;
status - string; General RPC error code. "OK" means everything looks good.
untrusted - boolean; States if the result is obtained using the bootstrap mode, and is therefore not trusted (true), or when the daemon is fully synced and thus handles the RPC locally (false).
*/

35
src/common/OutKey.ts Normal file
View file

@ -0,0 +1,35 @@
export class OutKey {
public readonly height: number;
public readonly key: string;
public readonly mask: string;
public readonly txId: string;
public readonly unlocked: boolean;
constructor(height: number, key: string, mask: string, txId: string, unlocked: boolean) {
this.height = height;
this.key = key;
this.mask = mask;
this.txId = txId;
this.unlocked = unlocked;
}
public static parse(outkey: any): OutKey {
const height = outkey.height;
const key = outkey.key;
const mask = outkey.mask;
const txId = outkey.txid;
const unlocked = outkey.unlocked;
return new OutKey(height, key, mask, txId, unlocked);
}
}
/**
* height - unsigned int; block height of the output
key - String; the public key of the output
mask - String
txid - String; transaction id
unlocked - boolean; States if output is locked (false) or not (true)
*/

16
src/common/Output.ts Normal file
View file

@ -0,0 +1,16 @@
export class Output {
public readonly amount: number;
public readonly index: number;
constructor(amount: number, index: number) {
this.amount = amount;
this.index = index;
}
public toDictionary(): { [key: string]: any } {
return {
'amount': this.amount,
'index': this.index
}
}
}

View file

@ -0,0 +1,22 @@
export class OutputDistribution {
public readonly amount: number;
public readonly base: number;
public readonly distribution: number[];
public readonly startHeight: number;
constructor(amount: number, base: number, distribution: number[], startHeight: number) {
this.amount = amount;
this.base = base;
this.distribution = distribution;
this.startHeight = startHeight;
}
public static parse(outDistribution: any): OutputDistribution {
const amount = outDistribution.amount;
const base = outDistribution.base;
const startHeight = outDistribution.start_height;
const distribution = outDistribution.distribution;
return new OutputDistribution(amount, base, startHeight, distribution);
}
}

31
src/common/PublicNode.ts Normal file
View file

@ -0,0 +1,31 @@
export class PublicNode {
public readonly host: string;
public readonly lastSeen: number;
public readonly rpcCreditsPerHash: number;
public readonly rpcPort: number;
public readonly type: 'white' | 'gray';
constructor(type: 'white' | 'gray', host: string, lastSeen: number, rpcCreditsPerHash: number, rpcPort: number) {
this.host = host;
this.lastSeen = lastSeen;
this.rpcCreditsPerHash = rpcCreditsPerHash;
this.rpcPort = rpcPort;
this.type = type;
}
public static parse(publicNode: any, nodeType: 'white' | 'gray'): PublicNode {
const host = publicNode.host;
const lastSeen = publicNode.last_seen;
const rpcCreditsPerHash = publicNode.rpc_credits_per_hash;
const rpcPort = publicNode.rpc_port;
return new PublicNode(nodeType, host, lastSeen, rpcCreditsPerHash, rpcPort);
}
}
/**
* host - string; The node's IP address. This includes IPv4, IPv6, Onion, and i2p addresses.
last_seen - unsigned int; UNIX timestamp of the last time the node was seen.
rpc_credits_per_hash - unsigned int; If payment for RPC is enabled, the number of credits the node is requesting per hash. Otherwise, 0.
rpc_port - unsigned int; RPC port number of the node.
*/

67
src/common/TxInfo.ts Normal file
View file

@ -0,0 +1,67 @@
export class TxInfo {
public readonly doubleSpend: boolean;
public readonly feeTooLow: boolean;
public readonly invalidInput: boolean;
public readonly invalidOutput: boolean;
public readonly lowMixin: boolean;
public readonly notRct: boolean;
public readonly notRelayed: boolean;
public readonly overspend: boolean;
public readonly reason: string;
public readonly tooBig: boolean;
constructor(
doubleSpend: boolean,
feeTooLow: boolean,
invalidInput: boolean,
invalidOutput: boolean,
lowMixin: boolean,
notRct: boolean,
notRelayed: boolean,
overspend: boolean,
reason: string,
tooBig: boolean
) {
this.doubleSpend = doubleSpend;
this.feeTooLow = feeTooLow;
this.invalidInput = invalidInput;
this.invalidOutput = invalidOutput;
this.lowMixin = lowMixin;
this.notRct = notRct;
this.notRelayed = notRelayed;
this.overspend = overspend;
this.reason = reason;
this.tooBig = tooBig;
}
public static parse(txInfo: any): TxInfo {
return new TxInfo(
txInfo.double_spend,
txInfo.fee_too_low,
txInfo.invalid_input,
txInfo.invalid_output,
txInfo.low_mixin,
txInfo.not_rct,
txInfo.not_relayed,
txInfo.overspend,
txInfo.reason,
txInfo.too_big
);
}
}
/**
* double_spend - boolean; Transaction is a double spend (true) or not (false).
fee_too_low - boolean; Fee is too low (true) or OK (false).
invalid_input - boolean; Input is invalid (true) or valid (false).
invalid_output - boolean; Output is invalid (true) or valid (false).
low_mixin - boolean; Mixin count is too low (true) or OK (false).
not_rct - boolean; Transaction is a standard ring transaction (true) or a ring confidential transaction (false).
not_relayed - boolean; Transaction was not relayed (true) or relayed (false).
overspend - boolean; Transaction uses more money than available (true) or not (false).
reason - string; Additional information. Currently empty or "Not relayed" if transaction was accepted but not relayed.
status - string; General RPC error code. "OK" means everything looks good. Any other value means that something went wrong.
too_big - boolean; Transaction size is too big (true) or OK (false).
untrusted - boolean; States if the result is obtained using the bootstrap mode, and is therefore not trusted (true), or when the daemon is fully synced and thus handles the RPC locally (false)
*/

28
src/common/UpdateInfo.ts Normal file
View file

@ -0,0 +1,28 @@
export class UpdateInfo {
public readonly autoUri: string;
public readonly hash: string;
public readonly path: string;
public readonly update: boolean;
public readonly userUri: string;
public readonly version: string;
constructor(autoUri: string, hash: string, path: string, update: boolean, userUri: string, version: string) {
this.autoUri = autoUri;
this.hash = hash;
this.path = path;
this.update = update;
this.userUri = userUri;
this.version = version;
}
public static parse(info: any): UpdateInfo {
const autoUri = info.auto_uri;
const hash = info.hash;
const path = info.path;
const update = info.update;
const userUri = info.user_uri;
const version = info.version;
return new UpdateInfo(autoUri, hash, path, update, userUri, version);
}
}

View file

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

View file

@ -0,0 +1,8 @@
import { RpcError } from "./RpcError";
export class MethodNotFoundError extends RpcError {
constructor() {
super(-32601, 'Method not found');
}
}

View file

@ -1,5 +1,6 @@
export { RPCRequest } from "./request/RPCRequest"; export { RPCRequest } from "./request/RPCRequest";
export { JsonRPCRequest } from "./request/JsonRPCRequest"; export { JsonRPCRequest } from "./request/JsonRPCRequest";
export { GetBlockRequest } from "./request/GetBlockRequest";
export { GetBlockCountRequest } from "./request/GetBlockCountRequest"; export { GetBlockCountRequest } from "./request/GetBlockCountRequest";
export { GetBlockHashRequest } from "./request/GetBlockHashRequest"; export { GetBlockHashRequest } from "./request/GetBlockHashRequest";
export { GetBlockTemplateRequest } from "./request/GetBlockTemplateRequest"; export { GetBlockTemplateRequest } from "./request/GetBlockTemplateRequest";
@ -16,7 +17,9 @@ export { SetBansRequest } from "./request/SetBansRequest";
export { GetBansRequest } from "./request/GetBansRequest"; export { GetBansRequest } from "./request/GetBansRequest";
export { BannedRequest } from "./request/BannedRequest"; export { BannedRequest } from "./request/BannedRequest";
export { FlushTxPoolRequest } from "./request/FlushTxPoolRequest"; export { FlushTxPoolRequest } from "./request/FlushTxPoolRequest";
export { GetOutsRequest } from "./request/GetOutsRequest";
export { GetOutputHistogramRequest } from "./request/GetOutputHistogramRequest"; export { GetOutputHistogramRequest } from "./request/GetOutputHistogramRequest";
export { GetOutputDistributionRequest } from "./request/GetOutputDistributionRequest";
export { SyncInfoRequest } from "./request/SyncInfoRequest"; export { SyncInfoRequest } from "./request/SyncInfoRequest";
export { GetVersionRequest } from "./request/GetVersionRequest"; export { GetVersionRequest } from "./request/GetVersionRequest";
export { GetFeeEstimateRequest } from "./request/GetFeeEstimateRequest"; export { GetFeeEstimateRequest } from "./request/GetFeeEstimateRequest";
@ -28,7 +31,29 @@ export { CalculatePoWHashRequest } from "./request/CalculatePoWHashRequest";
export { FlushCacheRequest } from "./request/FlushCacheRequest"; export { FlushCacheRequest } from "./request/FlushCacheRequest";
export { GetMinerDataRequest } from "./request/GetMinerDataRequest"; export { GetMinerDataRequest } from "./request/GetMinerDataRequest";
export { GetCoinbaseTxSumRequest } from "./request/GetCoinbaseTxSumRequest"; export { GetCoinbaseTxSumRequest } from "./request/GetCoinbaseTxSumRequest";
export { AddAuxPoWRequest } from "./request/AddAuxPoWRequest";
export { EmptyRpcRequest } from "./request/EmptyRpcRequest"; export { EmptyRpcRequest } from "./request/EmptyRpcRequest";
export { UpdateRequest } from "./request/UpdateRequest";
export { CheckUpdateRequest } from "./request/CheckUpdateRequest";
export { DownloadUpdateRequest } from "./request/DownloadUpdateRequest";
export { PopBlocksRequest } from "./request/PopBlocksRequest";
export { GetTransactionPoolHashesRequest } from "./request/GetTransactionPoolHashesRequest";
export { GetTransactionPoolHashesBinaryRequest } from "./request/GetTransactionPoolHashesBinaryRequest";
export { GetPublicNodesRequest } from "./request/GetPublicNodesRequest";
export { GetNetStatsRequest } from "./request/GetNetStatsRequest";
export { InPeersRequest } from "./request/InPeersRequest";
export { OutPeersRequest } from "./request/OutPeersRequest";
export { SetLimitRequest } from "./request/SetLimitRequest";
export { StopDaemonRequest } from "./request/StopDaemonRequest";
export { MiningStatusRequest } from "./request/MiningStatusRequest";
export { StartMiningRequest } from "./request/StartMiningRequest";
export { StopMiningRequest } from "./request/StopMiningRequest";
export { SendRawTransactionRequest } from "./request/SendRawTransactionRequest";
export { IsKeyImageSpentRequest } from "./request/IsKeyImageSpentRequest";
export { GetAltBlockHashesRequest } from "./request/GetAltBlockHashesRequest";
export { SaveBcRequest } from "./request/SaveBcRequest";
export { SetBootstrapDaemonRequest } from "./request/SetBootstrapDaemonRequest";
/** /**
* Restricted requests * Restricted requests

View file

@ -0,0 +1,30 @@
import { AuxPoW } from "../AuxPoW";
import { JsonRPCRequest } from "./JsonRPCRequest";
export class AddAuxPoWRequest extends JsonRPCRequest {
public override readonly method: string = 'add_aux_pow';
public override readonly restricted: boolean = true;
public readonly blockTemplateBlob: string;
public readonly auxPoW: AuxPoW[];
constructor(blockTemplateBlob: string, auxPoW: AuxPoW[]) {
super();
this.blockTemplateBlob = blockTemplateBlob;
this.auxPoW = auxPoW;
}
public override toDictionary(): { [key: string]: any; } {
const dict = super.toDictionary();
const auxPow: { [key: string]: any }[] = [];
this.auxPoW.forEach((pow) => auxPow.push(pow.toDictionary()));
dict['params'] = {
'blocktemplate_blob': this.blockTemplateBlob,
'aux_pow': auxPow
}
return dict;
}
}

View file

@ -0,0 +1,9 @@
import { UpdateRequest } from "./UpdateRequest";
export class CheckUpdateRequest extends UpdateRequest {
public override readonly command: "check" = "check";
constructor() {
super("check", '');
}
}

View file

@ -0,0 +1,10 @@
import { UpdateRequest } from "./UpdateRequest";
export class DownloadUpdateRequest extends UpdateRequest {
public override readonly command: "download" = "download";
constructor(path: string = '') {
super("download", path);
}
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class GetAltBlockHashesRequest extends RPCRequest {
public override readonly method: 'get_alt_block_hashes' = 'get_alt_block_hashes';
public override readonly restricted: false = false;
constructor() {
super();
}
public override toDictionary(): { [key: string]: any; } {
return {};
}
}

View file

@ -0,0 +1,54 @@
import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetBlockRequest extends JsonRPCRequest {
public override readonly method: string = 'get_block';
public override readonly restricted: boolean = false;
public readonly height: number;
public readonly hash: string;
public readonly fillPoWHash: boolean;
constructor(heightOrHash: number | string, fillPoWHash: boolean = false) {
super();
if (typeof heightOrHash == 'number') {
this.height = heightOrHash;
this.hash = '';
}
else if (typeof heightOrHash == 'string') {
this.hash = heightOrHash;
this.height = -1;
}
else {
throw new Error('Invalid paramater heightOrHash, must be a number or a string');
}
this.fillPoWHash = fillPoWHash;
}
public get byHash(): boolean {
return this.hash != '' && this.height < 0;
}
public get byHeight(): boolean {
return this.hash == '' && this.height >= 0;
}
public override toDictionary(): { [key: string]: any; } {
const dict = super.toDictionary();
let params: { [key: string]: any } = { 'fill_pow_hash': this.fillPoWHash };
if (this.byHeight) {
params['height'] = this.height;
}
else if (this.byHash) {
params['hash'] = this.hash;
}
dict['params'] = params;
return dict;
}
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class GetNetStatsRequest extends RPCRequest {
public override readonly method: 'get_net_stats' = 'get_net_stats';
public override readonly restricted: boolean = false;
constructor() {
super();
}
public override toDictionary(): { [key: string]: any; } {
return { };
}
}

View file

@ -0,0 +1,33 @@
import { JsonRPCRequest } from "./JsonRPCRequest";
export class GetOutputDistributionRequest extends JsonRPCRequest {
public override readonly method: string = 'get_output_distribution';
public override readonly restricted: boolean = false;
public readonly amounts: number[];
public readonly cumulative: boolean;
public readonly fromHeight: number;
public readonly toHeight: number;
constructor(amounts: number[], cumulative: boolean, fromHeight: number, toHeight: number) {
super();
this.amounts = amounts;
this.cumulative = cumulative;
this.fromHeight = fromHeight;
this.toHeight = toHeight;
}
public override toDictionary(): { [key: string]: any; } {
const dict = super.toDictionary();
dict['params'] = {
'amounts': this.amounts,
'cumulative': this.cumulative,
'from_height': this.fromHeight,
'to_height': this.toHeight
};
return dict;
}
}

View file

@ -0,0 +1,26 @@
import { Output } from "../Output";
import { RPCRequest } from "./RPCRequest";
export class GetOutsRequest extends RPCRequest {
public override readonly method: string = 'get_outs';
public override readonly restricted: boolean = false;
public readonly outputs: Output[];
public readonly getTxId: boolean;
constructor(outputs: Output[], getTxId: boolean) {
super();
this.outputs = outputs;
this.getTxId = getTxId;
}
public override toDictionary(): { [key: string]: any; } {
const outputs: { [key: string]: any }[] = [];
this.outputs.forEach((output) => outputs.push(output.toDictionary()))
return {
'outputs': outputs,
'get_txid': this.getTxId
}
}
}

View file

@ -0,0 +1,26 @@
import { RPCRequest } from "./RPCRequest";
export class GetPublicNodesRequest extends RPCRequest {
public override readonly method: 'get_public_nodes' = 'get_public_nodes';
public override readonly restricted: boolean = false;
public readonly whites: boolean;
public readonly grays: boolean;
public readonly includeBlocked: boolean;
constructor(whites: boolean = true, grays: boolean = false, includeBlocked: boolean = false) {
super();
this.whites = whites;
this.grays = grays;
this.includeBlocked = includeBlocked;
}
public override toDictionary(): { [key: string]: any; } {
return {
'whites': this.whites,
'grays': this.grays,
'include_blocked': this.includeBlocked
};
}
}

View file

@ -0,0 +1,14 @@
import { RPCBinaryRequest } from "./RPCBinaryRequest";
export class GetTransactionPoolHashesBinaryRequest extends RPCBinaryRequest {
public override readonly method: 'get_transaction_pool_hashes.bin' = 'get_transaction_pool_hashes.bin';
public override readonly restricted: boolean = false;
constructor() {
super();
}
public override toDictionary(): { [key: string]: any; } {
return { };
}
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class GetTransactionPoolHashesRequest extends RPCRequest {
public override readonly method: 'get_transaction_pool_hashes' = 'get_transaction_pool_hashes';
public override readonly restricted: boolean = false;
constructor() {
super();
}
public override toDictionary(): { [key: string]: any; } {
return { };
}
}

View file

@ -0,0 +1,20 @@
import { RPCRequest } from "./RPCRequest";
export class InPeersRequest extends RPCRequest {
public override readonly method: 'in_peers' = 'in_peers';
public override readonly restricted: false = false;
public readonly inPeers: number;
constructor(inPeers: number) {
super();
this.inPeers = inPeers;
}
public override toDictionary(): { [key: string]: any; } {
return {
"in_peers": this.inPeers
};
}
}

View file

@ -0,0 +1,19 @@
import { RPCRequest } from "./RPCRequest";
export class IsKeyImageSpentRequest extends RPCRequest {
public override readonly method: 'is_key_image_spent' = 'is_key_image_spent';
public override readonly restricted: false = false;
public readonly keyImages: string[];
constructor(keyImages: string[]) {
super();
this.keyImages = keyImages;
}
public override toDictionary(): { [key: string]: any; } {
return {
'key_images': this.keyImages
}
}
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class MiningStatusRequest extends RPCRequest {
public override readonly method: 'mining_status' = 'mining_status';
public override readonly restricted: true = true;
constructor() {
super();
}
public override toDictionary(): { [key: string]: any; } {
return {};
}
}

View file

@ -0,0 +1,20 @@
import { RPCRequest } from "./RPCRequest";
export class OutPeersRequest extends RPCRequest {
public override readonly method: 'out_peers' = 'out_peers';
public override readonly restricted: false = false;
public readonly outPeers: number;
constructor(outPeers: number) {
super();
this.outPeers = outPeers;
}
public override toDictionary(): { [key: string]: any; } {
return {
"out_peers": this.outPeers
};
}
}

View file

@ -0,0 +1,20 @@
import { RPCRequest } from "./RPCRequest";
export class PopBlocksRequest extends RPCRequest {
public override readonly method: string = 'pop_blocks';
public override readonly restricted: boolean = false;
public readonly nBlocks: number;
constructor(nBlocks: number) {
super();
this.nBlocks = nBlocks;
}
public override toDictionary(): { [key: string]: any; } {
return {
'nblocks': this.nBlocks
}
}
}

View file

@ -0,0 +1,5 @@
import { RPCRequest } from "./RPCRequest";
export abstract class RPCBinaryRequest extends RPCRequest {
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class SaveBcRequest extends RPCRequest {
public override readonly method: 'save_bc' = 'save_bc';
public override readonly restricted: true = true;
constructor() {
super();
}
public toDictionary(): { [key: string]: any; } {
return {};
}
}

View file

@ -0,0 +1,22 @@
import { RPCRequest } from "./RPCRequest";
export class SendRawTransactionRequest extends RPCRequest {
public override readonly method: 'send_raw_transaction' = 'send_raw_transaction';
public override readonly restricted: false = false;
public readonly txAsHex: string;
public readonly doNotRelay: boolean;
constructor(txAsHex: string, doNotRelay: boolean = false) {
super();
this.txAsHex = txAsHex;
this.doNotRelay = doNotRelay;
}
public override toDictionary(): { [key: string]: any; } {
return {
"tx_as_hex": this.txAsHex,
"do_not_relay": this.doNotRelay
}
}
}

View file

@ -0,0 +1,28 @@
import { RPCRequest } from "./RPCRequest";
export class SetBootstrapDaemonRequest extends RPCRequest {
public override readonly method: 'set_bootstrap_daemon' = 'set_bootstrap_daemon';
public override readonly restricted: true = true;
public readonly address: string;
public readonly username: string;
public readonly password: string;
public readonly proxy: string;
constructor(address: string, username: string, password: string, proxy: string) {
super();
this.address = address;
this.username = username;
this.password = password;
this.proxy = proxy;
}
public override toDictionary(): { [key: string]: any; } {
return {
'address': this.address,
'username': this.username,
'password': this.password,
'proxy': this.proxy
}
}
}

View file

@ -0,0 +1,22 @@
import { RPCRequest } from "./RPCRequest"
export class SetLimitRequest extends RPCRequest {
public override readonly method: 'set_limit' = 'set_limit';
public override readonly restricted: true = true;
public readonly limitDown: number;
public readonly limitUp: number;
constructor(limitDown: number, limitUp: number) {
super();
this.limitDown = limitDown;
this.limitUp = limitUp;
}
public override toDictionary(): { [key: string]: any; } {
return {
'limit_down': this.limitDown,
'limit_up': this.limitUp
}
}
}

View file

@ -0,0 +1,27 @@
import { RPCRequest } from "./RPCRequest";
export class StartMiningRequest extends RPCRequest {
public override readonly method: 'start_mining' = 'start_mining';
public override readonly restricted: true = true;
public readonly doBackgroundMining: boolean;
public readonly ignoreBattery: boolean;
public readonly minerAddress: string;
public readonly threadsCount: number;
constructor(doBackgroundMining: boolean, ignoreBattery: boolean, minerAddress: string, threadsCount: number) {
super();
this.doBackgroundMining = doBackgroundMining;
this.ignoreBattery = ignoreBattery;
this.minerAddress = minerAddress;
this.threadsCount = threadsCount;
}
public override toDictionary(): { [key: string]: any; } {
return {
"do_background_mining": this.doBackgroundMining,
"ignore_battery": this.ignoreBattery,
"miner_address": this.minerAddress,
"threads_count": this.threadsCount
}
}
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class StopDaemonRequest extends RPCRequest {
public override readonly method: 'stop_daemon' = 'stop_daemon';
public override readonly restricted: true = true;
constructor() {
super();
}
public override toDictionary(): { [key: string]: any; } {
return {};
}
}

View file

@ -0,0 +1,14 @@
import { RPCRequest } from "./RPCRequest";
export class StopMiningRequest extends RPCRequest {
public override readonly method: 'stop_mining' = 'stop_mining';
public override readonly restricted: true = true;
constructor() {
super();
}
public toDictionary(): { [key: string]: any; } {
return {};
}
}

View file

@ -0,0 +1,27 @@
import { RPCRequest } from "./RPCRequest";
export class UpdateRequest extends RPCRequest {
public override readonly method: string = 'update';
public override readonly restricted: boolean = true;
public readonly command: 'check' | 'download';
public readonly path: string;
constructor(command: 'check' | 'download', path: string = '') {
super();
this.command = command;
this.path = path;
}
public override toDictionary(): { [key: string]: any; } {
const dict: { [key: string]: any } = {
'command': this.command
};
if (this.path != '') {
dict['path'] = this.path;
}
return dict;
}
}

View file

@ -9,6 +9,6 @@
<link rel="icon" type="image/x-icon" href="assets/icons/favicon.ico"> <link rel="icon" type="image/x-icon" href="assets/icons/favicon.ico">
</head> </head>
<body> <body>
<app-root>Loading...</app-root> <app-root></app-root>
</body> </body>
</html> </html>