From 475421e7f6b9f985ab99a20ceb4212af0c5008e9 Mon Sep 17 00:00:00 2001 From: Duddino Date: Tue, 11 Mar 2025 10:53:38 +0100 Subject: [PATCH 1/2] Change wallet events to being local --- scripts/composables/use_wallet.js | 24 +++++++++- scripts/dashboard/Activity.vue | 1 - scripts/dashboard/Dashboard.vue | 2 +- scripts/dashboard/WalletBalance.vue | 51 +++++++++----------- scripts/event_bus.js | 12 ++++- scripts/mempool.js | 19 +++++++- scripts/sapling_params.js | 5 +- scripts/wallet.js | 74 +++++++++++++++++++++-------- 8 files changed, 129 insertions(+), 59 deletions(-) diff --git a/scripts/composables/use_wallet.js b/scripts/composables/use_wallet.js index 335311e05..3b913f1f7 100644 --- a/scripts/composables/use_wallet.js +++ b/scripts/composables/use_wallet.js @@ -84,7 +84,7 @@ export const useWallet = defineStore('wallet', () => { pendingShieldBalance.value = await wallet.getPendingShieldBalance(); isSynced.value = wallet.isSynced; }; - getEventEmitter().on('shield-loaded-from-disk', () => { + wallet.onShieldLoadedFromDisk(() => { hasShield.value = wallet.hasShield(); }); const createAndSendTransaction = lockableFunction( @@ -166,7 +166,7 @@ export const useWallet = defineStore('wallet', () => { publicMode.value = fPublicMode; }); - getEventEmitter().on('balance-update', async () => { + wallet.onBalanceUpdate(async () => { balance.value = wallet.balance; immatureBalance.value = wallet.immatureBalance; immatureColdBalance.value = wallet.immatureColdBalance; @@ -183,6 +183,22 @@ export const useWallet = defineStore('wallet', () => { blockCount.value = rawBlockCount; }); + const onNewTx = (fun) => { + return wallet.onNewTx(fun); + }; + + const onTransparentSyncStatusUpdate = (fun) => { + return wallet.onTransparentSyncStatusUpdate(fun); + }; + + const onShieldSyncStatusUpdate = (fun) => { + return wallet.onShieldSyncStatusUpdate(fun); + }; + + const onShieldTransactionCreationUpdate = (fun) => { + return wallet.onShieldTransactionCreationUpdate(fun); + }; + return { publicMode, isImported, @@ -221,5 +237,9 @@ export const useWallet = defineStore('wallet', () => { blockCount, lockCoin, unlockCoin, + onNewTx, + onTransparentSyncStatusUpdate, + onShieldSyncStatusUpdate, + onShieldTransactionCreationUpdate, }; }); diff --git a/scripts/dashboard/Activity.vue b/scripts/dashboard/Activity.vue index fc3fdf2bc..1137c5038 100644 --- a/scripts/dashboard/Activity.vue +++ b/scripts/dashboard/Activity.vue @@ -7,7 +7,6 @@ import { translation } from '../i18n.js'; import { Database } from '../database.js'; import { HistoricalTx, HistoricalTxType } from '../historical_tx.js'; import { getNameOrAddress } from '../contacts-book.js'; -import { getEventEmitter } from '../event_bus'; import iCheck from '../../assets/icons/icon-check.svg'; import iHourglass from '../../assets/icons/icon-hourglass.svg'; diff --git a/scripts/dashboard/Dashboard.vue b/scripts/dashboard/Dashboard.vue index 337e6c889..486ad632a 100644 --- a/scripts/dashboard/Dashboard.vue +++ b/scripts/dashboard/Dashboard.vue @@ -453,7 +453,7 @@ getEventEmitter().on('sync-status', (status) => { if (status === 'stop') activity?.value?.update(); }); -getEventEmitter().on('new-tx', () => { +wallet.onNewTx(() => { activity?.value?.update(); }); diff --git a/scripts/dashboard/WalletBalance.vue b/scripts/dashboard/WalletBalance.vue index 68992a43e..bd5629ad1 100644 --- a/scripts/dashboard/WalletBalance.vue +++ b/scripts/dashboard/WalletBalance.vue @@ -3,7 +3,7 @@ import { cChainParams, COIN } from '../chain_params.js'; import { translation, tr } from '../i18n'; import { ref, computed, toRefs } from 'vue'; import { beautifyNumber } from '../misc'; -import { getEventEmitter } from '../event_bus'; +import { useWallet } from '../composables/use_wallet'; import { optimiseCurrencyLocale } from '../global'; import { renderWalletBreakdown } from '../charting.js'; import { guiRenderCurrentReceiveModal } from '../contacts-book'; @@ -61,6 +61,8 @@ const { publicMode, } = toRefs(props); +const wallet = useWallet(); + // Transparent sync status const transparentSyncing = ref(false); const percentage = ref(0.0); @@ -129,35 +131,28 @@ const emit = defineEmits([ 'restoreWallet', ]); -getEventEmitter().on( - 'transparent-sync-status-update', - (i, totalPages, finished) => { - const str = tr(translation.syncStatusHistoryProgress, [ - { current: totalPages - i + 1 }, - { total: totalPages }, - ]); - const progress = ((totalPages - i) / totalPages) * 100; - syncTStr.value = str; - percentage.value = progress; - transparentSyncing.value = !finished; - } -); +wallet.onTransparentSyncStatusUpdate((i, totalPages, finished) => { + const str = tr(translation.syncStatusHistoryProgress, [ + { current: totalPages - i + 1 }, + { total: totalPages }, + ]); + const progress = ((totalPages - i) / totalPages) * 100; + syncTStr.value = str; + percentage.value = progress; + transparentSyncing.value = !finished; +}); -getEventEmitter().on( - 'shield-sync-status-update', - (bytes, totalBytes, finished) => { - percentage.value = Math.round((100 * bytes) / totalBytes); - const mb = bytes / 1_000_000; - const totalMb = totalBytes / 1_000_000; - shieldSyncingStr.value = `Syncing Shield (${mb.toFixed( - 1 - )}MB/${totalMb.toFixed(1)}MB)`; - shieldSyncing.value = !finished; - } -); +wallet.onShieldSyncStatusUpdate((bytes, totalBytes, finished) => { + percentage.value = Math.round((100 * bytes) / totalBytes); + const mb = bytes / 1_000_000; + const totalMb = totalBytes / 1_000_000; + shieldSyncingStr.value = `Syncing Shield (${mb.toFixed( + 1 + )}MB/${totalMb.toFixed(1)}MB)`; + shieldSyncing.value = !finished; +}); -getEventEmitter().on( - 'shield-transaction-creation-update', +wallet.onShieldTransactionCreationUpdate( // state: 0 = loading shield params // 1 = proving tx // 2 = finished diff --git a/scripts/event_bus.js b/scripts/event_bus.js index ff0e8eccf..c59f0b66b 100644 --- a/scripts/event_bus.js +++ b/scripts/event_bus.js @@ -4,7 +4,7 @@ import { EventEmitter } from 'events'; * Wrapper class around EventEmitter that allow to enable/disable specific events. * By defaults all events are enabled, and can be disabled by calling disableEvent */ -class EventEmitterWrapper { +export class EventEmitterWrapper { /** @type{EventEmitter} */ #internalEmitter; /** @@ -16,8 +16,16 @@ class EventEmitterWrapper { this.#internalEmitter = new EventEmitter(); } + /** + * @param {string} eventName + * @param {Function} listener + * @returns {()=>void} A function that removes the attached listener when called + */ on(eventName, listener) { - this.#internalEmitter.on(eventName, listener); + const a = this.#internalEmitter.on(eventName, listener); + return () => { + a.removeListener(eventName, listener); + }; } emit(eventName, ...args) { diff --git a/scripts/mempool.js b/scripts/mempool.js index 96d0855e1..86d642a94 100644 --- a/scripts/mempool.js +++ b/scripts/mempool.js @@ -1,4 +1,3 @@ -import { getEventEmitter } from './event_bus.js'; import { COutpoint, UTXO } from './transaction.js'; export const OutpointState = { @@ -31,6 +30,22 @@ export class Mempool { immatureColdBalance: new CachableBalance(), }; + /** + * @type{Function} + */ + #emitter; + + /** + * @param {Function} emitter - Calling this function emits the balance-update event + */ + constructor(emitter = () => {}) { + this.#emitter = emitter; + } + + setEmitter(emitter) { + this.#emitter = emitter; + } + /** * Add a transaction to the mempool * And mark the input as spent. @@ -238,7 +253,7 @@ export class Mempool { this.#balances.immatureBalance.invalidate(); this.#balances.balance.invalidate(); this.#balances.coldBalance.invalidate(); - getEventEmitter().emit('balance-update'); + this.#emitter(); } /** diff --git a/scripts/sapling_params.js b/scripts/sapling_params.js index 519b6b857..f9b77ae2c 100644 --- a/scripts/sapling_params.js +++ b/scripts/sapling_params.js @@ -39,7 +39,7 @@ export class SaplingParams { /** * @param {import('pivx-shield').PIVXShield} shield */ - async fetch(shield) { + async fetch(shield, emitter = () => {}) { if (await this.#getFromDatabase(shield)) return; const streams = [ { @@ -64,8 +64,7 @@ export class SaplingParams { const { done, value } = await reader.read(); if (value) { percentage += (100 * ratio * value.length) / totalBytes; - getEventEmitter().emit( - 'shield-transaction-creation-update', + emitter( percentage, // state: 0 = loading shield params // 1 = proving tx diff --git a/scripts/wallet.js b/scripts/wallet.js index 209138616..b96daad1a 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -24,7 +24,7 @@ import { } from './utils.js'; import { LedgerController } from './ledger.js'; import { OutpointState, Mempool } from './mempool.js'; -import { getEventEmitter } from './event_bus.js'; +import { EventEmitterWrapper, getEventEmitter } from './event_bus.js'; import { lockableFunction } from './lock.js'; import { isP2CS, @@ -126,6 +126,8 @@ export class Wallet { * @type {number} */ #lastProcessedBlock = 0; + + #eventEmitter = new EventEmitterWrapper(); /** * Array of historical txs, ordered by block height * @type OrderedArray @@ -143,6 +145,9 @@ export class Wallet { constructor({ nAccount, masterKey, shield, mempool = new Mempool() }) { this.#nAccount = nAccount; this.#mempool = mempool; + this.#mempool.setEmitter(() => { + this.#eventEmitter.emit('balance-update'); + }); this.#masterKey = masterKey; this.#shield = shield; this.#iterChains((chain) => { @@ -796,8 +801,8 @@ export class Wallet { } // While syncing the wallet ( DB read + network sync) disable the event balance-update // This is done to avoid a huge spam of event. - getEventEmitter().disableEvent('balance-update'); - getEventEmitter().disableEvent('new-tx'); + this.#eventEmitter.disableEvent('balance-update'); + this.#eventEmitter.disableEvent('new-tx'); await this.#loadShieldFromDisk(); await this.#loadFromDisk(); @@ -819,10 +824,10 @@ export class Wallet { this.#updateCurrentAddress(); // Update both activities post sync - getEventEmitter().enableEvent('balance-update'); - getEventEmitter().enableEvent('new-tx'); - getEventEmitter().emit('balance-update'); - getEventEmitter().emit('new-tx'); + this.#eventEmitter.enableEvent('balance-update'); + this.#eventEmitter.enableEvent('new-tx'); + this.#eventEmitter.emit('balance-update'); + this.#eventEmitter.emit('new-tx'); }); async #transparentSync() { @@ -835,7 +840,7 @@ export class Wallet { // Compute the total pages and iterate through them until we've synced everything const totalPages = await cNet.getNumPages(nStartHeight, addr); for (let i = totalPages; i > 0; i--) { - getEventEmitter().emit( + this.#eventEmitter.emit( 'transparent-sync-status-update', i, totalPages, @@ -848,7 +853,7 @@ export class Wallet { await this.addTransaction(tx, tx.blockHeight === -1); } } - getEventEmitter().emit('transparent-sync-status-update', '', '', true); + this.#eventEmitter.emit('transparent-sync-status-update', '', '', true); } /** @@ -871,7 +876,7 @@ export class Wallet { let txs = []; const length = reader.contentLength; /** @type {Uint8Array} Array of bytes that we are processing **/ - getEventEmitter().emit( + this.#eventEmitter.emit( 'shield-sync-status-update', 0, length, @@ -906,7 +911,7 @@ export class Wallet { handleBlocksTime += performance.now() - start; blocksArray = []; // Emit status update - getEventEmitter().emit( + this.#eventEmitter.emit( 'shield-sync-status-update', reader.readBytes, length, @@ -959,7 +964,7 @@ export class Wallet { await this.#checkShieldSaplingRoot(networkSaplingRoot); this.#isSynced = true; - getEventEmitter().emit('shield-sync-status-update', 0, 0, true); + this.#eventEmitter.emit('shield-sync-status-update', 0, 0, true); } /** @@ -990,14 +995,14 @@ export class Wallet { } #subscribeToNetworkEvents() { - getEventEmitter().on('new-block', async (block) => { + this.#eventEmitter.on('new-block', async (block) => { if (this.#isSynced) { await this.#getLatestBlocks(block); // Invalidate the balance cache to keep immature balance updated this.#mempool.invalidateBalanceCache(); // Emit a new-tx signal to update the Activity. // Otherwise, unconfirmed txs would not get updated - getEventEmitter().emit('new-tx'); + this.#eventEmitter.emit('new-tx'); } }); } @@ -1082,7 +1087,7 @@ export class Wallet { } const loadRes = await PIVXShield.load(cAccount.shieldData); this.#shield = loadRes.pivxShield; - getEventEmitter().emit('shield-loaded-from-disk'); + this.#eventEmitter.emit('shield-loaded-from-disk'); // Load operation was not successful! // Provided data are not compatible with the latest PIVX shield version. // Resetting the shield object is required @@ -1265,7 +1270,7 @@ export class Wallet { const periodicFunction = new AsyncInterval(async () => { const percentage = (await this.#shield.getTxStatus()) * 100; - getEventEmitter().emit( + this.#eventEmitter.emit( 'shield-transaction-creation-update', percentage, // state: 0 = loading shield params @@ -1296,7 +1301,7 @@ export class Wallet { throw e; } finally { await periodicFunction.clearInterval(); - getEventEmitter().emit( + this.#eventEmitter.emit( 'shield-transaction-creation-update', 0.0, // state: 0 = loading shield params @@ -1312,7 +1317,12 @@ export class Wallet { getNetwork(), await Database.getInstance() ); - await params.fetch(this.#shield); + await params.fetch(this.#shield, (...args) => { + this.#eventEmitter.emit( + 'shield-transaction-creation-update', + ...args + ); + }); } /** @@ -1378,8 +1388,8 @@ export class Wallet { this.#updateCurrentAddress(); } await this.#pushToHistoricalTx(transaction); - getEventEmitter().emit('new-tx'); - getEventEmitter().emit('balance-update'); + this.#eventEmitter.emit('new-tx'); + this.#eventEmitter.emit('balance-update'); } /** @@ -1485,6 +1495,30 @@ export class Wallet { await this.addTransaction(tx, true); } } + + onNewTx(fun) { + return this.#eventEmitter.on('new-tx', fun); + } + + onBalanceUpdate(fun) { + return this.#eventEmitter.on('balance-update', fun); + } + + onTransparentSyncStatusUpdate(fun) { + return this.#eventEmitter.on('transparent-sync-status-update', fun); + } + + onShieldSyncStatusUpdate(fun) { + return this.#eventEmitter.on('shield-sync-status-update', fun); + } + + onShieldLoadedFromDisk(fun) { + return this.#eventEmitter.on('shield-loaded-from-disk', fun); + } + + onShieldTransactionCreationUpdate(fun) { + return this.#eventEmitter.on('shield-transaction-creation-update', fun); + } } /** From e3e0ee9322b5aa7cec95348798a4e52bf34b42fb Mon Sep 17 00:00:00 2001 From: Duddino Date: Tue, 11 Mar 2025 14:07:17 +0100 Subject: [PATCH 2/2] Fix tests --- scripts/wallet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/wallet.js b/scripts/wallet.js index b96daad1a..657150cad 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -995,7 +995,7 @@ export class Wallet { } #subscribeToNetworkEvents() { - this.#eventEmitter.on('new-block', async (block) => { + getEventEmitter().on('new-block', async (block) => { if (this.#isSynced) { await this.#getLatestBlocks(block); // Invalidate the balance cache to keep immature balance updated