diff --git a/src/app/core/services/daemon/daemon-data.service.ts b/src/app/core/services/daemon/daemon-data.service.ts index 435f2fc..6f1bdb4 100644 --- a/src/app/core/services/daemon/daemon-data.service.ts +++ b/src/app/core/services/daemon/daemon-data.service.ts @@ -1,6 +1,6 @@ import { EventEmitter, Injectable, NgZone } from '@angular/core'; import { DaemonService } from './daemon.service'; -import { BlockCount, BlockHeader, Chain, Connection, CoreIsBusyError, DaemonInfo, MinerData, MiningStatus, NetStats, NetStatsHistory, PeerInfo, ProcessStats, PublicNode, SyncInfo, TimeUtils, TxBacklogEntry, TxPool } from '../../../../common'; +import { BlockCount, BlockHeader, Chain, Connection, CoreIsBusyError, DaemonInfo, MinerData, MiningStatus, NetStats, NetStatsHistory, PeerInfo, ProcessStats, PublicNode, SyncInfo, TimeUtils, TxBacklogEntry, TxPool, TxPoolStats } from '../../../../common'; import { ElectronService } from '../electron/electron.service'; @Injectable({ @@ -64,6 +64,9 @@ export class DaemonDataService { private _txPoolBacklog: TxBacklogEntry[] = []; private _gettingTxPoolBackLog: boolean = false; + private _txPoolStats?: TxPoolStats; + private _gettingTxPoolStats: boolean = false; + public readonly syncStart: EventEmitter<{ first: boolean }> = new EventEmitter<{ first: boolean }>(); public readonly syncEnd: EventEmitter = new EventEmitter(); public readonly syncError: EventEmitter = new EventEmitter(); @@ -242,6 +245,14 @@ export class DaemonDataService { return this._gettingTxPoolBackLog; } + public get txPoolStats(): TxPoolStats | undefined { + return this._txPoolStats; + } + + public get gettingTxPoolStats(): boolean { + return this._gettingTxPoolStats; + } + public setRefreshTimeout(ms: number = 5000): void { this.refreshTimeoutMs = ms; } @@ -481,6 +492,15 @@ export class DaemonDataService { this._transactionPool = undefined; } + if (this._daemonInfo.synchronized) { + this._gettingTxPoolStats = true; + this._txPoolStats = await this.daemonService.getTransactionPoolStats(); + this._gettingTxPoolStats = false; + } + else { + this._txPoolStats = undefined; + } + this._gettingConnections = true; this._connections = await this.daemonService.getConnections(); this._gettingConnections = false; @@ -500,6 +520,7 @@ export class DaemonDataService { this._gettingTransactionPool = false; this._gettingConnections = false; this._gettingPeerList = false; + this._gettingTxPoolStats = false; this.syncError.emit(error); diff --git a/src/app/core/services/daemon/daemon.service.ts b/src/app/core/services/daemon/daemon.service.ts index 32d0cce..6848f76 100644 --- a/src/app/core/services/daemon/daemon.service.ts +++ b/src/app/core/services/daemon/daemon.service.ts @@ -43,7 +43,8 @@ import { SetLogHashRateRequest, SetLogCategoriesRequest, GetTransactionPoolRequest, - GetPeerListRequest + GetPeerListRequest, + GetTransactionPoolStatsRequest } from '../../../../common/request'; import { BlockTemplate } from '../../../../common/BlockTemplate'; import { GeneratedBlocks } from '../../../../common/GeneratedBlocks'; @@ -78,7 +79,7 @@ import { TxInfo } from '../../../../common/TxInfo'; import { DaemonSettings } from '../../../../common/DaemonSettings'; import { MethodNotFoundError } from '../../../../common/error/MethodNotFoundError'; import { openDB, IDBPDatabase } from "idb" -import { PeerInfo, ProcessStats, TimeUtils, TxPool } from '../../../../common'; +import { PeerInfo, ProcessStats, TimeUtils, TxPool, TxPoolStats } from '../../../../common'; import { MoneroInstallerService } from '../monero-installer/monero-installer.service'; @Injectable({ @@ -1090,6 +1091,16 @@ export class DaemonService { return TxPool.parse(response); } + public async getTransactionPoolStats(): Promise { + const response = await this.callRpc(new GetTransactionPoolStatsRequest()); + + if (typeof response.status == 'string' && response.status != 'OK') { + throw new Error(response.status); + } + + return TxPoolStats.parse(response); + } + public async getTransactionPoolHashes(): Promise { const response = await this.callRpc(new GetTransactionPoolHashesRequest()); const txHashes: string[] = response.tx_hashes; diff --git a/src/app/pages/transactions/transactions.component.html b/src/app/pages/transactions/transactions.component.html index 420e977..25b68c0 100644 --- a/src/app/pages/transactions/transactions.component.html +++ b/src/app/pages/transactions/transactions.component.html @@ -17,59 +17,111 @@

Information about valid transactions seen by the node but not yet mined into a block, as well as spent key image information for the txpool in the node's memory


-
Transactions
-
- - - - - - - - - - - - - - - - - - - -
ID HashRelayedBlob SizeDo Not RelayDouble Spend SeenFeeKept By BlockLast Failed HeightLast Failed Id HashLast Relayed TimeMax Used Block HeightMax Used Block Id HashReceive TimeTx Blob
+
+
+

Statistics

+
+
+
+
+
Bytes
+
    +
  • Bytes Max: {{ txPoolStats.bytesMax }} bytes
  • +
  • Bytes Med: {{ txPoolStats.bytesMed }} units
  • +
  • Bytes Min: {{ txPoolStats.bytesMin }}
  • +
  • Bytes Total: {{ txPoolStats.bytesTotal }}
  • +
+
+
+
Histo
+
    +
  • Histo Txs: {{ txPoolStats.histo.txs }}
  • +
  • Histo Bytes: {{ txPoolStats.histo.bytes }}
  • +
  • Histo 98% (the time 98% of txes are "younger" than): {{ txPoolStats.histo98pc }}
  • +
+
+
+ +
+
+
Info
+
    +
  • Transactions in pool for more than 10 minutes: {{ txPoolStats.num10m }}
  • +
  • Double spend transactions: {{ txPoolStats.numDoubleSpends }}
  • +
  • Failing transactions: {{ txPoolStats.numFailing }}
  • +
  • Non-relayed transactions: {{ txPoolStats.numNotRelayed }}
  • +
  • Oldest transaction in the pool: {{ txPoolStats.oldest }}
  • +
  • Total number of transactions: {{ txPoolStats.txsTotal }}
  • +
+
+
+ +
+
+
Transactions in Pool
+
  • +
    + + + + + + + + + + + + + + + + + + + +
    ID HashRelayedBlob SizeDo Not RelayDouble Spend SeenFeeKept By BlockLast Failed HeightLast Failed Id HashLast Relayed TimeMax Used Block HeightMax Used Block Id HashReceive TimeTx Blob
    +
    +
  • +
    +
    + +
    +
    +
    Spent Key Images in Pool
    +
  • +
    + + + + + + +
    ID Hash
    +
    +
  • +
    +
    + + +
    - -
    - -
    Spent Key Images
    -
    - - - - - - -
    ID Hash
    -
    -
    @@ -256,13 +308,13 @@
    -
    +
    Block height from which getting the amounts
    -
    +
    Number of blocks to include in the sum @@ -293,7 +345,7 @@
    -
    +

    Flush a list of transaction IDs

    @@ -336,7 +388,7 @@
    -
    +

    Flush bad transactions / blocks from the cache

    diff --git a/src/app/pages/transactions/transactions.component.ts b/src/app/pages/transactions/transactions.component.ts index bf0c161..ddb4bcd 100644 --- a/src/app/pages/transactions/transactions.component.ts +++ b/src/app/pages/transactions/transactions.component.ts @@ -5,7 +5,7 @@ import { NavbarLink } from '../../shared/components/navbar/navbar.model'; import { TxBacklogEntry } from '../../../common/TxBacklogEntry'; import { SimpleBootstrapCard } from '../../shared/utils'; import { DaemonDataService } from '../../core/services'; -import { FeeEstimate, SpentKeyImage, UnconfirmedTx } from '../../../common'; +import { FeeEstimate, SpentKeyImage, TxPoolHisto, TxPoolStats, UnconfirmedTx } from '../../../common'; import { Subscription } from 'rxjs'; import { BasePageComponent } from '../base-page/base-page.component'; @@ -76,6 +76,14 @@ export class TransactionsComponent extends BasePageComponent implements AfterVie return this.daemonData.transactionPool.spentKeyImages; } + public get txPoolStats(): TxPoolStats { + if (this.daemonData.txPoolStats) { + return this.daemonData.txPoolStats; + } + + return new TxPoolStats(0, 0, 0, 0, 0, new TxPoolHisto(0, 0), 0, 0, 0, 0, 0, 0, 0) + } + constructor(private daemonData: DaemonDataService, private daemonService: DaemonService, navbarService: NavbarService, private ngZone: NgZone) { super(navbarService); @@ -99,12 +107,21 @@ export class TransactionsComponent extends BasePageComponent implements AfterVie this.ngZone.run(() => { this.loadTables(); - const onSyncEndSub: Subscription = this.daemonData.syncEnd.subscribe(() => this.loadTables()); + const onSyncEndSub: Subscription = this.daemonData.syncEnd.subscribe(() => this.refresh()); this.subscriptions.push(onSyncEndSub); }); } + private refresh(): void { + this.loadTables(); + this.loadPoolStats(); + } + + private loadPoolStats(): void { + + } + private loadTransactionsTable(): void { this.loadTable('transactionsTable', this.unconfirmedTxs); } diff --git a/src/common/TxPoolStats.ts b/src/common/TxPoolStats.ts new file mode 100644 index 0000000..4dd4e7f --- /dev/null +++ b/src/common/TxPoolStats.ts @@ -0,0 +1,78 @@ +export class TxPoolStats { + public readonly bytesMax: number; + public readonly bytesMed: number; + public readonly bytesMin: number; + public readonly bytesTotal: number; + + public readonly feeTotal: number; + + public readonly histo: TxPoolHisto; + public readonly histo98pc: number; + + public readonly num10m: number; + public readonly numDoubleSpends: number; + public readonly numFailing: number; + public readonly numNotRelayed: number; + public readonly oldest: number; + public readonly txsTotal: number; + + constructor(bytesMax: number, bytesMed: number, bytesMin: number, bytesTotal: number, + feeTotal: number, histo: TxPoolHisto, histo98pc: number, num10m: number, + numDoubleSpends: number, numFailing: number, numNotRelayed: number, oldest: number, + txsTotal: number + ) { + this.bytesMax = bytesMax; + this.bytesMed = bytesMed; + this.bytesMin = bytesMin; + this.bytesTotal = bytesTotal; + this.feeTotal = feeTotal; + this.histo = histo; + this.histo98pc = histo98pc; + this.num10m = num10m; + this.numDoubleSpends = numDoubleSpends; + this.numFailing = numFailing; + this.numNotRelayed = numNotRelayed; + this.oldest = oldest; + this.txsTotal = txsTotal; + } + + public static parse(txPoolStats: any): TxPoolStats { + const bytesMax: number = txPoolStats.bytes_max; + const bytesMed: number = txPoolStats.bytes_med; + const bytesMin: number = txPoolStats.bytes_min; + const bytesTotal: number = txPoolStats.bytes_total; + const feeTotal: number = txPoolStats.total_fee; + const histo: TxPoolHisto = TxPoolHisto.parse(txPoolStats.histo); + const histo98pc: number = txPoolStats.histo98pc; + const num10m: number = txPoolStats.num10m; + const numDoubleSpends: number = txPoolStats.num_double_spends; + const numFailing: number = txPoolStats.num_failing; + const numNotRelayed: number = txPoolStats.num_not_relayed; + const oldest: number = txPoolStats.oldest; + const txsTotal: number = txPoolStats.txs_total; + + return new TxPoolStats(bytesMax, bytesMed, bytesMin, bytesTotal, + feeTotal, histo, histo98pc, num10m, + numDoubleSpends, numFailing, numNotRelayed, oldest, + txsTotal); + } + +} + +export class TxPoolHisto { + public readonly txs: number; + public readonly bytes: number; + + constructor(txs: number, bytes: number) { + this.txs = txs; + this.bytes = bytes; + } + + public static parse(object: any): TxPoolHisto { + const txs: number = object.txs; + const bytes: number = object.bytes; + + return new TxPoolHisto(txs, bytes); + } + +} \ No newline at end of file diff --git a/src/common/index.ts b/src/common/index.ts index ef96a9d..4180d30 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -38,6 +38,7 @@ export { NetStatsHistory, NetStatsHistoryEntry } from './NetStatsHistory'; export { UnconfirmedTx } from './UnconfirmedTx'; export { SpentKeyImage } from './SpentKeyImage'; export { TxPool } from './TxPool'; +export { TxPoolStats, TxPoolHisto } from './TxPoolStats'; export { ProcessStats } from './ProcessStats'; export * from './error'; diff --git a/src/common/request/GetTransactionPoolStatsRequest.ts b/src/common/request/GetTransactionPoolStatsRequest.ts new file mode 100644 index 0000000..375692b --- /dev/null +++ b/src/common/request/GetTransactionPoolStatsRequest.ts @@ -0,0 +1,12 @@ +import { RPCRequest } from "./RPCRequest"; + + +export class GetTransactionPoolStatsRequest extends RPCRequest { + public override readonly method: string = 'get_transaction_pool_stats'; + public override readonly restricted: boolean = false; + + public toDictionary(): { [key: string]: any; } { + return { }; + } + +} \ No newline at end of file diff --git a/src/common/request/index.ts b/src/common/request/index.ts index 0e8b131..6f94f77 100644 --- a/src/common/request/index.ts +++ b/src/common/request/index.ts @@ -60,6 +60,7 @@ export { SetLogCategoriesRequest } from "./SetLogCategoriesRequest"; export { GetTransactionPoolRequest } from "./GetTransactionPoolRequest"; export { GetPeerListRequest } from "./GetPeerListRequest"; export { GetTransactionsRequest } from "./GetTransactionsRequest"; +export { GetTransactionPoolStatsRequest } from "./GetTransactionPoolStatsRequest"; /** * Restricted requests