diff --git a/app/main.ts b/app/main.ts index 85e8a31..7dad2cb 100644 --- a/app/main.ts +++ b/app/main.ts @@ -96,22 +96,13 @@ function execMoneroDaemon(configFilePath: string): ChildProcess { return monerodProcess; } -function startMoneroDaemon(configFilePath: string): ChildProcessWithoutNullStreams { +function startMoneroDaemon(commandOptions: string[]): ChildProcessWithoutNullStreams { const monerodPath = path.resolve(__dirname, monerodFilePath); - - const args = [ - '--testnet', - '--fast-block-sync', '1', - '--prune-blockchain', - '--sync-pruned-blocks', - '--confirm-external-bind', - '--max-concurrency', '1', - '--log-level', '1', - '--rpc-access-control-origins=*' - ]; + + console.log("Starting monerod daemon with options: " + commandOptions.join(" ")); // Avvia il processo usando spawn - const monerodProcess = spawn(monerodPath, args); + const monerodProcess = spawn(monerodPath, commandOptions); // Gestisci l'output di stdout in streaming monerodProcess.stdout.on('data', (data) => { diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index a16d40c..536d2a8 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -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 { SettingsModule } from './pages/settings/settings.module'; import { TransactionsModule } from './pages/transactions/transactions.module'; +import { FormsModule } from '@angular/forms'; const routes: Routes = [ { @@ -23,6 +24,7 @@ const routes: Routes = [ @NgModule({ imports: [ RouterModule.forRoot(routes, {}), + FormsModule, HomeRoutingModule, DetailRoutingModule, TransactionsModule, diff --git a/src/app/app.component.html b/src/app/app.component.html index 7836fbc..dba58c2 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,6 @@
- +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index a953acb..bdcb6dc 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ElectronService } from './core/services'; import { TranslateService } from '@ngx-translate/core'; import { APP_CONFIG } from '../environments/environment'; +import { DaemonService } from './core/services/daemon/daemon.service'; @Component({ selector: 'app-root', @@ -9,9 +10,13 @@ import { APP_CONFIG } from '../environments/environment'; styleUrls: ['./app.component.scss'] }) export class AppComponent { + public loading: boolean; + public daemonRunning: boolean; + constructor( private electronService: ElectronService, - private translate: TranslateService + private translate: TranslateService, + private daemonService: DaemonService ) { this.translate.setDefaultLang('en'); console.log('APP_CONFIG', APP_CONFIG); @@ -24,5 +29,22 @@ export class AppComponent { } else { console.log('Run in browser'); } + + this.loading = false; + this.daemonRunning = false; + this.load(); + } + + private async load(): Promise { + this.loading = true; + + try { + this.daemonRunning = await this.daemonService.isRunning(); + } + catch(error) { + console.error(error); + } + + this.loading = false; } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bb5cca1..5fdd19e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -22,6 +22,7 @@ import { MiningModule } from './pages/mining/mining.module'; import { TransactionsModule } from './pages/transactions/transactions.module'; import { OutputsModule } from './pages/outputs/outputs.module'; import { SidebarComponent } from './shared/components'; +import { SettingsModule } from './pages/settings/settings.module'; // AoT requires an exported function for factories const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json'); @@ -40,6 +41,7 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl MiningModule, TransactionsModule, OutputsModule, + SettingsModule, TranslateModule, AppRoutingModule, TranslateModule.forRoot({ diff --git a/src/app/core/services/daemon/daemon.service.ts b/src/app/core/services/daemon/daemon.service.ts index 575db26..16636a3 100644 --- a/src/app/core/services/daemon/daemon.service.ts +++ b/src/app/core/services/daemon/daemon.service.ts @@ -7,8 +7,8 @@ import { SubmitBlockRequest, GenerateBlocksRequest, GetLastBlockHeaderRequest, GetBlockHeaderByHashRequest, GetBlockHeaderByHeightRequest, GetBlockHeadersRangeRequest, GetConnectionsRequest, GetInfoRequest, HardForkInfoRequest, SetBansRequest, GetBansRequest, - BannedRequest, FlushTxPoolRequest, GetOutputHistogramRequest, - SyncInfoRequest, + BannedRequest, FlushTxPoolRequest, GetOutputHistogramRequest, GetCoinbaseTxSumRequest, + SyncInfoRequest, GetOutsRequest, GetVersionRequest, GetFeeEstimateRequest, GetAlternateChainsRequest, @@ -17,7 +17,28 @@ import { CalculatePoWHashRequest, FlushCacheRequest, 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'; import { BlockTemplate } from '../../../../common/BlockTemplate'; import { GeneratedBlocks } from '../../../../common/GeneratedBlocks'; @@ -37,14 +58,28 @@ import { BlockchainPruneInfo } from '../../../../common/BlockchainPruneInfo'; import { MinerData } from '../../../../common/MinerData'; import { CoreIsBusyError } from '../../../../common/error'; 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({ providedIn: 'root' }) export class DaemonService { private daemonRunning?: boolean; - private readonly configFilePath: string = './config'; private url: string = "http://127.0.0.1:28081"; + public settings: DaemonSettings; //private url: string = "http://node2.monerodevs.org:28089"; //private url: string = "https://testnet.xmr.ditatompel.com"; //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 }; - 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 }> { - return await firstValueFrom<{ [key: string]: any }>(this.httpClient.post(`${this.url}/json_rpc`, params.toDictionary(), this.headers)); + private loadSettings(): DaemonSettings { + /* + 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 { @@ -74,7 +165,7 @@ export class DaemonService { 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"); @@ -88,39 +179,52 @@ export class DaemonService { return this.daemonRunning; } - const response = await this.callJsonRpc(new EmptyRpcRequest()); - console.log(response); - this.daemonRunning = true; + await this.callRpc(new EmptyRpcRequest()); } catch(error) { + if (error instanceof MethodNotFoundError) { + this.daemonRunning = true; + return this.daemonRunning; + } + console.error(error); - this.daemonRunning = false; } - + + this.daemonRunning = false; return this.daemonRunning; } + public async getBlock(heightOrHash: number | string, fillPowHash: boolean = false): Promise { + 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 { - const response = await this.callJsonRpc(new GetBlockCountRequest()); + const response = await this.callRpc(new GetBlockCountRequest()); return BlockCount.parse(response.result); } public async getBlockHash(blockHeight: number): Promise { - const response = await this.callJsonRpc(new GetBlockHashRequest(blockHeight)); + const response = await this.callRpc(new GetBlockHashRequest(blockHeight)); return response.result; } 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); } public async submitBlock(... blockBlobData: string[]): Promise { - const response = await this.callJsonRpc(new SubmitBlockRequest(blockBlobData)); + const response = await this.callRpc(new SubmitBlockRequest(blockBlobData)); if (response.error) { if (!response.message) { @@ -132,31 +236,31 @@ export class DaemonService { } public async generateBlocks(amountOfBlocks: number, walletAddress: string, prevBlock: string = '', startingNonce: number): Promise { - 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); } public async getLastBlockHeader(fillPowHash: boolean = false): Promise { - const response = await this.callJsonRpc(new GetLastBlockHeaderRequest(fillPowHash)); + const response = await this.callRpc(new GetLastBlockHeaderRequest(fillPowHash)); return BlockHeader.parse(response.block_header); } public async getBlockHeaderByHash(hash: string, fillPowHash: boolean = false): Promise { - const response = await this.callJsonRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash)); + const response = await this.callRpc(new GetBlockHeaderByHashRequest(hash, fillPowHash)); return BlockHeader.parse(response.block_header); } public async getBlockHeaderByHeight(height: number, fillPowHash: boolean = false): Promise { - const response = await this.callJsonRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash)); + const response = await this.callRpc(new GetBlockHeaderByHeightRequest(height, fillPowHash)); return BlockHeader.parse(response.block_header); } public async getBlockHeadersRange(startHeight: number, endHeight: number, fillPowHash: boolean = false): Promise { - 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 result: BlockHeader[] = []; @@ -166,7 +270,7 @@ export class DaemonService { } public async getConnections(): Promise { - const response = await this.callJsonRpc(new GetConnectionsRequest()); + const response = await this.callRpc(new GetConnectionsRequest()); const connections: any[] = response.connections; const result: Connection[] = []; @@ -176,19 +280,19 @@ export class DaemonService { } public async getInfo(): Promise { - const response = await this.callJsonRpc(new GetInfoRequest()); + const response = await this.callRpc(new GetInfoRequest()); return DaemonInfo.parse(response.result); } public async hardForkInfo(): Promise { - const response = await this.callJsonRpc(new HardForkInfoRequest()); + const response = await this.callRpc(new HardForkInfoRequest()); return HardForkInfo.parse(response.result); } 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') { throw new Error(`Error code: ${response.status}`); @@ -196,7 +300,7 @@ export class DaemonService { } public async getBans(): Promise { - const response = await this.callJsonRpc(new GetBansRequest()); + const response = await this.callRpc(new GetBansRequest()); if (response.error) { this.raiseRpcError(response.error); @@ -211,7 +315,7 @@ export class DaemonService { } public async banned(address: string): Promise { - const response = await this.callJsonRpc(new BannedRequest(address)); + const response = await this.callRpc(new BannedRequest(address)); const result = response.result; if (result.status != 'OK') { @@ -222,43 +326,78 @@ export class DaemonService { } public async flushTxPool(... txIds: string[]): Promise { - const response = await this.callJsonRpc(new FlushTxPoolRequest(txIds)); + const response = await this.callRpc(new FlushTxPoolRequest(txIds)); if (response.status != 'OK') { throw new Error(`Error code: ${response.status}`); } } + public async getOuts(outputs: Output[], getTxId: boolean): Promise { + 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 { - const response = await this.callJsonRpc(new GetOutputHistogramRequest(amounts, minCount, maxCount, unlocked, recentCutoff)); - const entries: any[] = response.histogram; + const response = await this.callRpc(new GetOutputHistogramRequest(amounts, minCount, maxCount, unlocked, recentCutoff)); + + if (response.error) { + this.raiseRpcError(response.error); + } + + const entries: any[] = response.result.histogram; 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; } + public async getOutputDistribution(amounts: number[], cumulative: boolean, fromHeight: number, toHeight: number): Promise { + 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 { - const response = await this.callJsonRpc(new SyncInfoRequest()); + const response = await this.callRpc(new SyncInfoRequest()); return SyncInfo.parse(response.result); } public async getVersion(): Promise { - const response = await this.callJsonRpc(new GetVersionRequest()); + const response = await this.callRpc(new GetVersionRequest()); return DaemonVersion.parse(response.result); } public async getFeeEstimate(): Promise { - const response = await this.callJsonRpc(new GetFeeEstimateRequest()); + const response = await this.callRpc(new GetFeeEstimateRequest()); return FeeEstimate.parse(response.result); } public async getAlternateChains(): Promise { - const response = await this.callJsonRpc(new GetAlternateChainsRequest()); + const response = await this.callRpc(new GetAlternateChainsRequest()); const chains: any[] = response.result.chains ? response.result.chains : []; const result: Chain[] = []; @@ -267,8 +406,18 @@ export class DaemonService { return result; } + public async getCoinbaseTxSum(height: number, count: number): Promise { + 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 { - const response = await this.callJsonRpc(new RelayTxRequest(txIds)); + const response = await this.callRpc(new RelayTxRequest(txIds)); if (response.result.status != 'OK') { throw new Error(`Error code: ${response.result.status}`); @@ -276,25 +425,25 @@ export class DaemonService { } public async getTxPoolBacklog(): Promise { - const response = await this.callJsonRpc(new GetTxPoolBacklogRequest()); + const response = await this.callRpc(new GetTxPoolBacklogRequest()); return TxBacklogEntry.fromBinary(response.backlog); } public async pruneBlockchain(check: boolean = false): Promise { - const response = await this.callJsonRpc(new PruneBlockchainRequest(check)); + const response = await this.callRpc(new PruneBlockchainRequest(check)); return BlockchainPruneInfo.parse(response.result); } public async calculatePoWHash(majorVersion: number, height: number, blockBlob: string, seedHash: string): Promise { - 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; } public async flushCache(badTxs: boolean = false, badBlocks: boolean = false): Promise { - const response = await this.callJsonRpc(new FlushCacheRequest(badTxs, badBlocks)); + const response = await this.callRpc(new FlushCacheRequest(badTxs, badBlocks)); if(response.result.status != 'OK') { throw new Error(`Error code: ${response.result.status}`); @@ -302,25 +451,151 @@ export class DaemonService { } public async getMinerData(): Promise { - const response = await this.callJsonRpc(new GetMinerDataRequest()); - - if (response.error) { - this.raiseRpcError(response.error); - } + const response = await this.callRpc(new GetMinerDataRequest()); return MinerData.parse(response.result); } - private raiseRpcError(error: { code: number, message: string }): void { + public async AddAuxPoW(blockTemplateBlob: string, auxPoW: AuxPoW[]): Promise { + const response = await this.callRpc(new AddAuxPoWRequest(blockTemplateBlob, auxPoW)); - if (error.code == -9) { - throw new CoreIsBusyError(); - } - else - { - throw new Error(error.message); - } + return AddedAuxPow.parse(response.result); + } + public async setBootstrapDaemon(address: string, username: string = '', password: string = '', proxy: string = ''): Promise { + 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 { + 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 { + const response = await this.callRpc(new GetAltBlockHashesRequest()); + + return response.blks_hashes; + } + + public async isKeyImageSpent(...keyImages: string[]): Promise { + const response = await this.callRpc(new IsKeyImageSpentRequest(keyImages)); + + return response.spent_status; + } + + public async sendRawTransaction(txAsHex: string, doNotRelay: boolean = false): Promise { + 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 { + 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 { + 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 { + const response = await this.callRpc(new MiningStatusRequest()); + + return MiningStatus.parse(response); + } + + public async stopDaemon(): Promise { + 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 { + const response = await this.callRpc(new InPeersRequest(inPeers)); + + return response.in_peers; + } + + public async outPeers(outPeers: number): Promise { + const response = await this.callRpc(new OutPeersRequest(outPeers)); + + return response.out_peers; + } + + public async getNetStats(): Promise { + const response = await this.callRpc(new GetNetStatsRequest()); + + return NetStats.parse(response); + } + + public async getPublicNodes(whites: boolean = true, grays: boolean = false, includeBlocked: boolean = false): Promise { + 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 { + const response = await this.callRpc(new GetTransactionPoolHashesRequest()); + + return response.tx_hashes; + } + + public async getTransactionPoolHashesBinary(): Promise { + const response = await this.callRpc(new GetTransactionPoolHashesBinaryRequest()); + + return response.tx_hashes; + } + + public async popBlocks(nBlocks: number): Promise { + const response = await this.callRpc(new PopBlocksRequest(nBlocks)); + + return response.height; + } + + public async update(command: 'check' | 'download', path: string = ''): Promise { + const response = await this.callRpc(new UpdateRequest(command, path)); + + return UpdateInfo.parse(response); + } + + public async checkUpdate(): Promise { + return await this.update('check'); + } + + public async downloadUpdate(path: string = ''): Promise { + return await this.update('download', path); } } diff --git a/src/app/pages/detail/detail.component.html b/src/app/pages/detail/detail.component.html index b4666ac..1d99170 100644 --- a/src/app/pages/detail/detail.component.html +++ b/src/app/pages/detail/detail.component.html @@ -4,7 +4,7 @@
-
+

Daemon not running

Start monero daemon

diff --git a/src/app/pages/detail/detail.component.ts b/src/app/pages/detail/detail.component.ts index 09e9998..75af5db 100644 --- a/src/app/pages/detail/detail.component.ts +++ b/src/app/pages/detail/detail.component.ts @@ -90,7 +90,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { console.log('DetailComponent AFTER VIEW INIT'); this.navbarService.setNavbarLinks(this.navbarLinks); - this.loadInterval = setInterval(() => { + setTimeout(() => { this.ngZone.run(() => { if (this.isLoading) { return; @@ -103,7 +103,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy { this.load(); }); - }, 5000); + }, 500); } ngOnDestroy(): void { diff --git a/src/app/pages/mining/mining.component.ts b/src/app/pages/mining/mining.component.ts index 2209904..75b5531 100644 --- a/src/app/pages/mining/mining.component.ts +++ b/src/app/pages/mining/mining.component.ts @@ -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-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-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) => { diff --git a/src/app/pages/settings/settings.component.html b/src/app/pages/settings/settings.component.html index bcde5a1..2778397 100644 --- a/src/app/pages/settings/settings.component.html +++ b/src/app/pages/settings/settings.component.html @@ -7,73 +7,73 @@
- +
Do not listen for peers, nor connect to any
- +
Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P
- +
Restrict RPC to view-only commands and do not return privacy sensitive data in RPC calls
- +
Confirm Bind IP is not a loopback (local) IP
- +
Ignore unsuccessful IPv4 bind for RPC
- +
Do not ban hosts on RPC errors
- + Specify IP to bind RPC server
- + 18081 for mainnet, 28081 for testnet, 38081 for stagenet
- + Specify username required for RPC server
- + Specify password required for RPC server
- + Specify a comma separated list of origins to allow cross origin resource sharing
@@ -85,14 +85,14 @@
- +
Allow IPv6 for RPC
- + Specify IPv6 address to bind RPC server
@@ -111,26 +111,26 @@
- +
Enable ZMQ RPC Server
- + IP for ZMQ RPC Server to listen on
- + 18082 for mainnet, 28082 for testnet, 38082 for stagenet
- + Address for ZMQ Pub
@@ -143,25 +143,25 @@
- + Restrict RPC to clients sending micropayment to this address
- + Restrict RPC to clients sending micropayment at this difficulty
- + Restrict RPC to clients sending micropayment, yelds that many credits per payment
- +
Allow free access from the loopback address (ie, the local host)
@@ -175,43 +175,43 @@
- + + +
- +
Allow user chain certificates
- +
Allow any peer certificate
- + Path to a PEM format private key
- + Path to a PEM format certificate
- + Path to file containing concatenated PEM format certificate(s) to replace system CA(s)
@@ -229,33 +229,33 @@
- +
Allow local ip add to peer list, mostly in debug process
- +
Ignore unsuccessful IPv4 bind for P2P
- + Interface for p2p network protocol
- + 18080 for mainnet, 28080 for testnet, 38080 for stagenet
- + External port for p2p network protocol (if port forwarding used with NAT)
@@ -266,13 +266,13 @@
- + Specify IPv6 address to bind RPC server
- + 18080 for mainnet, 28080 for testnet, 38080 for stagenet
@@ -293,13 +293,13 @@
- + URL of a bootstrap remote daemon that the connected wallets can use while this daemon is still not fully synced.
- + Specify username for the bootstrap daemon login
@@ -312,7 +312,7 @@
- + Socks proxy to use for bootstrap daemon connection
@@ -325,59 +325,59 @@
- +
Synchronize the blockchain with other peers
- +
Reduce blockchain disk usage
- +
Allow syncing from nodes with only pruned blocks
- +
Sync up most of the way by using embedded, known block hashes
- +
Relay blocks as fluffy blocks
- +
Keep alternative blocks on restart
- +
- +
- +

@@ -386,59 +386,20 @@
- +
- +
- +
- - - -
- +
@@ -454,33 +415,33 @@
- +
Enable background mining
- +
Reduce blockchain disk usage
- + Specify min lookback interval in seconds for determining idle state
- + Specify minimum avg idle percentage over lookback interval
- + Specify maximum percentage cpu use by miners
@@ -501,24 +462,24 @@
- + + + + +
- + Specify maximum log file size [B]
- + Specify maximum number of rotated log files to be saved (no limit by setting to 0)
@@ -530,6 +491,6 @@
- + diff --git a/src/app/pages/settings/settings.component.ts b/src/app/pages/settings/settings.component.ts index c949b9a..645463a 100644 --- a/src/app/pages/settings/settings.component.ts +++ b/src/app/pages/settings/settings.component.ts @@ -2,17 +2,27 @@ import { AfterViewInit, Component } from '@angular/core'; import { NavbarService } from '../../shared/components/navbar/navbar.service'; import { NavigationEnd, NavigationStart, Router } from '@angular/router'; import { NavbarLink } from '../../shared/components/navbar/navbar.model'; +import { DaemonSettings } from '../../../common/DaemonSettings'; +import { FormsModule, NgModel } from '@angular/forms'; @Component({ selector: 'app-settings', templateUrl: './settings.component.html', - styleUrl: './settings.component.scss' + styleUrl: './settings.component.scss', + imports: [FormsModule], + standalone: true }) export class SettingsComponent implements AfterViewInit { private readonly navbarLinks: NavbarLink[]; + private originalSettings: DaemonSettings; + public currentSettings: DaemonSettings; + + public rpcLoginUser: string; + public rpcLoginPassword: string; constructor(private router: Router, private navbarService: NavbarService) { + this.navbarLinks = [ 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-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) => { 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 { 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 { } + + 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') diff --git a/src/app/pages/settings/settings.module.ts b/src/app/pages/settings/settings.module.ts index 57a9864..754242d 100644 --- a/src/app/pages/settings/settings.module.ts +++ b/src/app/pages/settings/settings.module.ts @@ -2,12 +2,16 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SettingsRoutingModule } from './settings-routing.module'; +import { SharedModule } from '../../shared/shared.module'; +import { FormsModule } from '@angular/forms'; @NgModule({ declarations: [], imports: [ CommonModule, + FormsModule, + SharedModule, SettingsRoutingModule ] }) diff --git a/src/app/pages/transactions/transactions.component.html b/src/app/pages/transactions/transactions.component.html index ac144a5..d8f8a99 100644 --- a/src/app/pages/transactions/transactions.component.html +++ b/src/app/pages/transactions/transactions.component.html @@ -1 +1,22 @@ -

transactions works!

+
+
+
+

Relay a list of transaction IDs

+
+
+ +
+ + + List of transaction IDs to relay +
+ +
+
+ +
+
+
+ +
+ diff --git a/src/app/pages/transactions/transactions.component.ts b/src/app/pages/transactions/transactions.component.ts index 49656d3..efb8700 100644 --- a/src/app/pages/transactions/transactions.component.ts +++ b/src/app/pages/transactions/transactions.component.ts @@ -11,6 +11,8 @@ import { NavbarLink } from '../../shared/components/navbar/navbar.model'; export class TransactionsComponent implements AfterViewInit { private readonly navbarLinks: NavbarLink[]; + public canRelay: boolean; + constructor(private daemonService: DaemonService, private navbarService: NavbarService) { this.navbarLinks = [ 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-cahe', '#pills-flush-cache', 'pills-flush-cache', false, 'Flush Cache', true) ]; + + this.canRelay = false; } ngAfterViewInit(): void { @@ -27,4 +31,8 @@ export class TransactionsComponent implements AfterViewInit { private async load(): Promise { } + + public async onRelay(): Promise { + + } } diff --git a/src/app/shared/components/sidebar/sidebar.component.ts b/src/app/shared/components/sidebar/sidebar.component.ts index 1dddf92..9b28063 100644 --- a/src/app/shared/components/sidebar/sidebar.component.ts +++ b/src/app/shared/components/sidebar/sidebar.component.ts @@ -1,5 +1,5 @@ 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'; @Component({ @@ -7,22 +7,32 @@ import { ChildActivationEnd, ChildActivationStart, NavigationCancel, NavigationE templateUrl: './sidebar.component.html', styleUrl: './sidebar.component.scss' }) -export class SidebarComponent { - public readonly navLinks: NavLink[]; +export class SidebarComponent implements OnChanges { + @Input() public isDaemonRunning: boolean = false; + + public navLinks: NavLink[]; public isLoading: boolean; public errorMessage: string; constructor(private router: Router) { - 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') - ]; + 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') + ]; + } this.isLoading = false; this.errorMessage = ''; } @@ -31,6 +41,27 @@ export class SidebarComponent { 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 { diff --git a/src/common/AddedAuxPow.ts b/src/common/AddedAuxPow.ts new file mode 100644 index 0000000..1e45342 --- /dev/null +++ b/src/common/AddedAuxPow.ts @@ -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); + } +} diff --git a/src/common/AuxPoW.ts b/src/common/AuxPoW.ts new file mode 100644 index 0000000..b062e9c --- /dev/null +++ b/src/common/AuxPoW.ts @@ -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); + } +} \ No newline at end of file diff --git a/src/common/Block.ts b/src/common/Block.ts new file mode 100644 index 0000000..db3f131 --- /dev/null +++ b/src/common/Block.ts @@ -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); + } +} \ No newline at end of file diff --git a/src/common/BlockDetails.ts b/src/common/BlockDetails.ts new file mode 100644 index 0000000..a7013c9 --- /dev/null +++ b/src/common/BlockDetails.ts @@ -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); + } +} diff --git a/src/common/CoinbaseTxSum.ts b/src/common/CoinbaseTxSum.ts new file mode 100644 index 0000000..65fa1fc --- /dev/null +++ b/src/common/CoinbaseTxSum.ts @@ -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); + } +} diff --git a/src/common/DaemonSettings.ts b/src/common/DaemonSettings.ts index a4fdc3a..28fdbd4 100644 --- a/src/common/DaemonSettings.ts +++ b/src/common/DaemonSettings.ts @@ -128,8 +128,121 @@ export class DaemonSettings { public rpcPaymentAllowFreeLoopback: boolean = false; public disableRpcBan: boolean = false; - public toCommandOptions(): string { - let options: string = ''; + public equals(settings: DaemonSettings): boolean { + 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; } diff --git a/src/common/MinerTx.ts b/src/common/MinerTx.ts new file mode 100644 index 0000000..8522b43 --- /dev/null +++ b/src/common/MinerTx.ts @@ -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); + } +} diff --git a/src/common/MiningStatus.ts b/src/common/MiningStatus.ts new file mode 100644 index 0000000..08c4794 --- /dev/null +++ b/src/common/MiningStatus.ts @@ -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" +} + */ + diff --git a/src/common/NetStats.ts b/src/common/NetStats.ts new file mode 100644 index 0000000..d4d412b --- /dev/null +++ b/src/common/NetStats.ts @@ -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). + */ \ No newline at end of file diff --git a/src/common/OutKey.ts b/src/common/OutKey.ts new file mode 100644 index 0000000..15eb35c --- /dev/null +++ b/src/common/OutKey.ts @@ -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) + */ \ No newline at end of file diff --git a/src/common/Output.ts b/src/common/Output.ts new file mode 100644 index 0000000..6f528b8 --- /dev/null +++ b/src/common/Output.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/OutputDistribution.ts b/src/common/OutputDistribution.ts new file mode 100644 index 0000000..e84e940 --- /dev/null +++ b/src/common/OutputDistribution.ts @@ -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); + } +} diff --git a/src/common/PublicNode.ts b/src/common/PublicNode.ts new file mode 100644 index 0000000..cb3a15a --- /dev/null +++ b/src/common/PublicNode.ts @@ -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. + */ \ No newline at end of file diff --git a/src/common/TxInfo.ts b/src/common/TxInfo.ts new file mode 100644 index 0000000..fb2e12b --- /dev/null +++ b/src/common/TxInfo.ts @@ -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) + */ \ No newline at end of file diff --git a/src/common/UpdateInfo.ts b/src/common/UpdateInfo.ts new file mode 100644 index 0000000..1937d22 --- /dev/null +++ b/src/common/UpdateInfo.ts @@ -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); + } +} \ No newline at end of file diff --git a/src/common/error.ts b/src/common/error.ts index 8319a49..854683a 100644 --- a/src/common/error.ts +++ b/src/common/error.ts @@ -1,2 +1,3 @@ export { RpcError } from "./error/RpcError"; export { CoreIsBusyError } from "./error/CoreIsBusyError"; +export { MethodNotFoundError } from "./error/MethodNotFoundError"; diff --git a/src/common/error/MethodNotFoundError.ts b/src/common/error/MethodNotFoundError.ts new file mode 100644 index 0000000..6156ccd --- /dev/null +++ b/src/common/error/MethodNotFoundError.ts @@ -0,0 +1,8 @@ +import { RpcError } from "./RpcError"; + +export class MethodNotFoundError extends RpcError { + + constructor() { + super(-32601, 'Method not found'); + } +} \ No newline at end of file diff --git a/src/common/request.ts b/src/common/request.ts index 791b58e..8e66788 100644 --- a/src/common/request.ts +++ b/src/common/request.ts @@ -1,5 +1,6 @@ export { RPCRequest } from "./request/RPCRequest"; export { JsonRPCRequest } from "./request/JsonRPCRequest"; +export { GetBlockRequest } from "./request/GetBlockRequest"; export { GetBlockCountRequest } from "./request/GetBlockCountRequest"; export { GetBlockHashRequest } from "./request/GetBlockHashRequest"; export { GetBlockTemplateRequest } from "./request/GetBlockTemplateRequest"; @@ -16,7 +17,9 @@ export { SetBansRequest } from "./request/SetBansRequest"; export { GetBansRequest } from "./request/GetBansRequest"; export { BannedRequest } from "./request/BannedRequest"; export { FlushTxPoolRequest } from "./request/FlushTxPoolRequest"; +export { GetOutsRequest } from "./request/GetOutsRequest"; export { GetOutputHistogramRequest } from "./request/GetOutputHistogramRequest"; +export { GetOutputDistributionRequest } from "./request/GetOutputDistributionRequest"; export { SyncInfoRequest } from "./request/SyncInfoRequest"; export { GetVersionRequest } from "./request/GetVersionRequest"; export { GetFeeEstimateRequest } from "./request/GetFeeEstimateRequest"; @@ -28,7 +31,29 @@ export { CalculatePoWHashRequest } from "./request/CalculatePoWHashRequest"; export { FlushCacheRequest } from "./request/FlushCacheRequest"; export { GetMinerDataRequest } from "./request/GetMinerDataRequest"; export { GetCoinbaseTxSumRequest } from "./request/GetCoinbaseTxSumRequest"; +export { AddAuxPoWRequest } from "./request/AddAuxPoWRequest"; 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 diff --git a/src/common/request/AddAuxPoWRequest.ts b/src/common/request/AddAuxPoWRequest.ts new file mode 100644 index 0000000..291eef6 --- /dev/null +++ b/src/common/request/AddAuxPoWRequest.ts @@ -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; + } +} \ No newline at end of file diff --git a/src/common/request/CheckUpdateRequest.ts b/src/common/request/CheckUpdateRequest.ts new file mode 100644 index 0000000..7c65935 --- /dev/null +++ b/src/common/request/CheckUpdateRequest.ts @@ -0,0 +1,9 @@ +import { UpdateRequest } from "./UpdateRequest"; + +export class CheckUpdateRequest extends UpdateRequest { + public override readonly command: "check" = "check"; + + constructor() { + super("check", ''); + } +} diff --git a/src/common/request/DownloadUpdateRequest.ts b/src/common/request/DownloadUpdateRequest.ts new file mode 100644 index 0000000..b0ff172 --- /dev/null +++ b/src/common/request/DownloadUpdateRequest.ts @@ -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); + } +} \ No newline at end of file diff --git a/src/common/request/GetAltBlockHashesRequest.ts b/src/common/request/GetAltBlockHashesRequest.ts new file mode 100644 index 0000000..7510ea1 --- /dev/null +++ b/src/common/request/GetAltBlockHashesRequest.ts @@ -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 {}; + } +} \ No newline at end of file diff --git a/src/common/request/GetBlockRequest.ts b/src/common/request/GetBlockRequest.ts new file mode 100644 index 0000000..9571f2a --- /dev/null +++ b/src/common/request/GetBlockRequest.ts @@ -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; + } + +} \ No newline at end of file diff --git a/src/common/request/GetNetStatsRequest.ts b/src/common/request/GetNetStatsRequest.ts new file mode 100644 index 0000000..d16678c --- /dev/null +++ b/src/common/request/GetNetStatsRequest.ts @@ -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 { }; + } +} \ No newline at end of file diff --git a/src/common/request/GetOutputDistributionRequest.ts b/src/common/request/GetOutputDistributionRequest.ts new file mode 100644 index 0000000..b3df18d --- /dev/null +++ b/src/common/request/GetOutputDistributionRequest.ts @@ -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; + } +} \ No newline at end of file diff --git a/src/common/request/GetOutsRequest.ts b/src/common/request/GetOutsRequest.ts new file mode 100644 index 0000000..b287f02 --- /dev/null +++ b/src/common/request/GetOutsRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/GetPublicNodesRequest.ts b/src/common/request/GetPublicNodesRequest.ts new file mode 100644 index 0000000..844dc4d --- /dev/null +++ b/src/common/request/GetPublicNodesRequest.ts @@ -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 + }; + } +} \ No newline at end of file diff --git a/src/common/request/GetTransactionPoolHashesBinaryRequest.ts b/src/common/request/GetTransactionPoolHashesBinaryRequest.ts new file mode 100644 index 0000000..de2aba9 --- /dev/null +++ b/src/common/request/GetTransactionPoolHashesBinaryRequest.ts @@ -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 { }; + } +} \ No newline at end of file diff --git a/src/common/request/GetTransactionPoolHashesRequest.ts b/src/common/request/GetTransactionPoolHashesRequest.ts new file mode 100644 index 0000000..349ba08 --- /dev/null +++ b/src/common/request/GetTransactionPoolHashesRequest.ts @@ -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 { }; + } +} \ No newline at end of file diff --git a/src/common/request/InPeersRequest.ts b/src/common/request/InPeersRequest.ts new file mode 100644 index 0000000..57d05f9 --- /dev/null +++ b/src/common/request/InPeersRequest.ts @@ -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 + }; + } +} \ No newline at end of file diff --git a/src/common/request/IsKeyImageSpentRequest.ts b/src/common/request/IsKeyImageSpentRequest.ts new file mode 100644 index 0000000..cd8c871 --- /dev/null +++ b/src/common/request/IsKeyImageSpentRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/MiningStatusRequest.ts b/src/common/request/MiningStatusRequest.ts new file mode 100644 index 0000000..314425f --- /dev/null +++ b/src/common/request/MiningStatusRequest.ts @@ -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 {}; + } +} \ No newline at end of file diff --git a/src/common/request/OutPeersRequest.ts b/src/common/request/OutPeersRequest.ts new file mode 100644 index 0000000..463c2ad --- /dev/null +++ b/src/common/request/OutPeersRequest.ts @@ -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 + }; + } +} \ No newline at end of file diff --git a/src/common/request/PopBlocksRequest.ts b/src/common/request/PopBlocksRequest.ts new file mode 100644 index 0000000..4bf30f7 --- /dev/null +++ b/src/common/request/PopBlocksRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/RPCBinaryRequest.ts b/src/common/request/RPCBinaryRequest.ts new file mode 100644 index 0000000..15ba323 --- /dev/null +++ b/src/common/request/RPCBinaryRequest.ts @@ -0,0 +1,5 @@ +import { RPCRequest } from "./RPCRequest"; + +export abstract class RPCBinaryRequest extends RPCRequest { + +} \ No newline at end of file diff --git a/src/common/request/SaveBcRequest.ts b/src/common/request/SaveBcRequest.ts new file mode 100644 index 0000000..818c0e6 --- /dev/null +++ b/src/common/request/SaveBcRequest.ts @@ -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 {}; + } +} \ No newline at end of file diff --git a/src/common/request/SendRawTransactionRequest.ts b/src/common/request/SendRawTransactionRequest.ts new file mode 100644 index 0000000..e00d582 --- /dev/null +++ b/src/common/request/SendRawTransactionRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/SetBootstrapDaemonRequest.ts b/src/common/request/SetBootstrapDaemonRequest.ts new file mode 100644 index 0000000..d5df2dc --- /dev/null +++ b/src/common/request/SetBootstrapDaemonRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/SetLimitRequest.ts b/src/common/request/SetLimitRequest.ts new file mode 100644 index 0000000..e4d3bcf --- /dev/null +++ b/src/common/request/SetLimitRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/StartMiningRequest.ts b/src/common/request/StartMiningRequest.ts new file mode 100644 index 0000000..b731981 --- /dev/null +++ b/src/common/request/StartMiningRequest.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/common/request/StopDaemonRequest.ts b/src/common/request/StopDaemonRequest.ts new file mode 100644 index 0000000..81d4102 --- /dev/null +++ b/src/common/request/StopDaemonRequest.ts @@ -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 {}; + } +} \ No newline at end of file diff --git a/src/common/request/StopMiningRequest.ts b/src/common/request/StopMiningRequest.ts new file mode 100644 index 0000000..1932196 --- /dev/null +++ b/src/common/request/StopMiningRequest.ts @@ -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 {}; + } +} \ No newline at end of file diff --git a/src/common/request/UpdateRequest.ts b/src/common/request/UpdateRequest.ts new file mode 100644 index 0000000..474dea1 --- /dev/null +++ b/src/common/request/UpdateRequest.ts @@ -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; + } +} \ No newline at end of file diff --git a/src/index.html b/src/index.html index 215a212..32ec579 100644 --- a/src/index.html +++ b/src/index.html @@ -9,6 +9,6 @@ - Loading... +