From 02daacd81dd63394202d248ff20f1a56830d6be7 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 4 Oct 2024 14:06:05 +0200 Subject: [PATCH 01/11] During the initial sync, add to the wallet txs that are decrypted by the Shield manager --- scripts/wallet.js | 56 +++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/scripts/wallet.js b/scripts/wallet.js index 60593dde..0d8ca3d2 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -736,14 +736,20 @@ export class Wallet { block = await cNet.getBlock(blockHeights[i], true); downloaded++; blocks[i] = block; - // We need to process blocks monotically + // We need to process blocks monotonically // When we get a block, start from the first unhandled // One and handle as many as possible for (let j = handled; blocks[j]; j = handled) { if (syncing) break; syncing = true; handled++; - await this.#shield.handleBlock(blocks[j]); + // Transactions belonging to the transparent wallet has already been added + // in the initial transparent sync. Therefore, set allowOwn = false. + await this.#handleBlock( + blocks[j], + blockHeights[j], + false + ); // Backup every 500 handled blocks if (handled % 500 == 0) await this.saveShieldOnDisk(); // Delete so we don't have to hold all blocks in memory @@ -831,25 +837,7 @@ export class Wallet { ) { try { block = await cNet.getBlock(blockHeight); - if (block.txs) { - if ( - this.hasShield() && - blockHeight > this.#shield.getLastSyncedBlock() - ) { - await this.#shield.handleBlock(block); - } - for (const tx of block.txs) { - const parsed = Transaction.fromHex(tx.hex); - parsed.blockHeight = blockHeight; - parsed.blockTime = tx.blocktime; - // Avoid wasting memory on txs that do not regard our wallet - if (this.ownTransaction(parsed)) { - await this.addTransaction(parsed); - } - } - } else { - break; - } + await this.#handleBlock(block, blockHeight); this.#lastProcessedBlock = blockHeight; } catch (e) { console.error(e); @@ -1156,6 +1144,32 @@ export class Wallet { } } + /** + * Handle the various transactions of a block + * @param block - block outputted from any PIVX node + * @param {number} blockHeight - the height of the block in the chain + * @param {boolean} allowOwn - whether to add transaction that satisfy ownTransaction() + */ + async #handleBlock(block, blockHeight, allowOwn = true) { + let shieldTxs = []; + if ( + this.hasShield() && + blockHeight > this.#shield.getLastSyncedBlock() + ) { + shieldTxs = await this.#shield.handleBlock(block); + } + for (const tx of block.txs) { + const parsed = Transaction.fromHex(tx.hex); + parsed.blockHeight = blockHeight; + parsed.blockTime = tx.blocktime; + // Avoid wasting memory on txs that do not regard our wallet + const isOwned = allowOwn ? this.ownTransaction(parsed) : false; + if (isOwned || shieldTxs.includes(tx.hex)) { + await this.addTransaction(parsed); + } + } + } + /** * Check if any vin or vout of the transaction belong to the wallet * @param {import('./transaction.js').Transaction} transaction From 5d6b8225422b270c336d0bf4b09f58383b77c22f Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 4 Oct 2024 14:07:05 +0200 Subject: [PATCH 02/11] Make getCredit and getDebit return whether all vins/vouts belongs to the wallet --- scripts/mempool.js | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/scripts/mempool.js b/scripts/mempool.js index a2ca7def..ea8a2944 100644 --- a/scripts/mempool.js +++ b/scripts/mempool.js @@ -117,13 +117,15 @@ export class Mempool { * @param {import('./transaction.js').Transaction} tx */ getDebit(tx) { - return tx.vin - .filter( - (input) => - this.getOutpointStatus(input.outpoint) & OutpointState.OURS - ) + const filteredVin = tx.vin.filter( + (input) => + this.getOutpointStatus(input.outpoint) & OutpointState.OURS + ); + const debit = filteredVin .map((i) => this.outpointToUTXO(i.outpoint)) .reduce((acc, u) => acc + (u?.value || 0), 0); + const ownAllVin = tx.vin.length == filteredVin.length; + return { debit, ownAllVin }; } /** @@ -133,17 +135,21 @@ export class Mempool { getCredit(tx) { const txid = tx.txid; - return tx.vout - .filter( - (_, i) => - this.getOutpointStatus( - new COutpoint({ - txid, - n: i, - }) - ) & OutpointState.OURS - ) - .reduce((acc, u) => acc + u?.value ?? 0, 0); + const filteredVout = tx.vout.filter( + (_, i) => + this.getOutpointStatus( + new COutpoint({ + txid, + n: i, + }) + ) & OutpointState.OURS + ); + const credit = filteredVout.reduce((acc, u) => acc + u?.value ?? 0, 0); + const ownAllVout = tx.vout.length == filteredVout.length; + return { + credit, + ownAllVout, + }; } /** From 2d2e8d28e6a15047cee057274e5694c320c5bf0b Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 4 Oct 2024 14:08:09 +0200 Subject: [PATCH 03/11] Update HistoricalTx class, to take in account shield data --- scripts/historical_tx.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/historical_tx.js b/scripts/historical_tx.js index 92883d9e..2e149be7 100644 --- a/scripts/historical_tx.js +++ b/scripts/historical_tx.js @@ -9,7 +9,9 @@ export class HistoricalTx { * @param {boolean} shieldedOutputs - If this transaction contains Shield outputs. * @param {number} time - The block time of the transaction. * @param {number} blockHeight - The block height of the transaction. - * @param {number} amount - The amount transacted, in coins. + * @param {number} amount - The transparent amount transacted, in coins. + * @param {number} shieldAmount - The shielded amount transacted, in coins. + * @param {boolean} isToSelf - If the transaction is to self. */ constructor( type, @@ -18,7 +20,9 @@ export class HistoricalTx { shieldedOutputs, time, blockHeight, - amount + amount, + shieldAmount, + isToSelf ) { this.type = type; this.id = id; @@ -27,6 +31,8 @@ export class HistoricalTx { this.time = time; this.blockHeight = blockHeight; this.amount = amount; + this.shieldAmount = shieldAmount; + this.isToSelf = isToSelf; } } From 4a22816f5b15334ecbcac4957cee1069ac65db12 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 4 Oct 2024 14:09:55 +0200 Subject: [PATCH 04/11] make toHistoricalTXs take in account shield data --- scripts/wallet.js | 67 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/scripts/wallet.js b/scripts/wallet.js index 0d8ca3d2..927302df 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -620,20 +620,24 @@ export class Wallet { /** * Convert a list of Blockbook transactions to HistoricalTxs * @param {import('./transaction.js').Transaction[]} arrTXs - An array of the Blockbook TXs - * @returns {Array} - A new array of `HistoricalTx`-formatted transactions + * @returns {Promise>} - A new array of `HistoricalTx`-formatted transactions */ - // TODO: add shield data to txs - toHistoricalTXs(arrTXs) { + async toHistoricalTXs(arrTXs) { let histTXs = []; for (const tx of arrTXs) { + const { credit, ownAllVout } = this.#mempool.getCredit(tx); + const { debit, ownAllVin } = this.#mempool.getDebit(tx); // The total 'delta' or change in balance, from the Tx's sums - let nAmount = - (this.#mempool.getCredit(tx) - this.#mempool.getDebit(tx)) / - COIN; + let nAmount = (credit - debit) / COIN; + // Shielded data + const { shieldCredit, shieldDebit, arrShieldReceivers } = + await this.extractSaplingAmounts(tx); + const nShieldAmount = (shieldCredit - shieldDebit) / COIN; + const ownAllShield = shieldDebit - shieldCredit === tx.valueBalance; // The receiver addresses, if any - let arrReceivers = this.getOutAddress(tx); - + let arrReceivers = + this.getOutAddress(tx).concat(arrShieldReceivers); const getFilteredCredit = (filter) => { return tx.vout .filter((_, i) => { @@ -661,9 +665,9 @@ export class Wallet { return addr[0] === cChainParams.current.STAKING_PREFIX; }); nAmount = getFilteredCredit(OutpointState.P2CS) / COIN; - } else if (nAmount > 0) { + } else if (nAmount + nShieldAmount > 0) { type = HistoricalTxType.RECEIVED; - } else if (nAmount < 0) { + } else if (nAmount + nShieldAmount < 0) { type = HistoricalTxType.SENT; } @@ -672,15 +676,54 @@ export class Wallet { type, tx.txid, arrReceivers, - false, + tx.hasShieldData, tx.blockTime, tx.blockHeight, - Math.abs(nAmount) + nAmount, + nShieldAmount, + ownAllVin && ownAllVout && ownAllShield ) ); } return histTXs; } + + /** + * Extract the sapling spent, received and shield addressed, regarding the wallet, from a tx + * @param {import('./transaction.js').Transaction} tx - a Transaction object + */ + async extractSaplingAmounts(tx) { + let shieldCredit = 0; + let shieldDebit = 0; + let arrShieldReceivers = []; + if (!tx.hasShieldData || !wallet.hasShield()) { + return { shieldCredit, shieldDebit, arrShieldReceivers }; + } + + for (const shieldSpend of tx.shieldSpend) { + //TODO: find a better way to reverse and swap endianess + const stringArr = shieldSpend.nullifier.split('').reverse(); + for (let i = 0; i < stringArr.length - 1; i += 2) { + const temp = stringArr[i + 1]; + stringArr[i + 1] = stringArr[i]; + stringArr[i] = temp; + } + const spentNote = this.#shield.getNoteFromNullifier( + stringArr.join('') + ); + if (spentNote) { + shieldDebit += spentNote.value; + } + } + const myOutputNotes = await this.#shield.decryptTransactionOutputs( + tx.serialize() + ); + for (const note of myOutputNotes) { + shieldCredit += note.value; + arrShieldReceivers.push(note.recipient); + } + return { shieldCredit, shieldDebit, arrShieldReceivers }; + } sync = lockableFunction(async () => { if (this.#isSynced) { throw new Error('Attempting to sync when already synced'); From d03eabeb4bf05116e8d70f4739ca4303a8978bc1 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 4 Oct 2024 14:10:26 +0200 Subject: [PATCH 05/11] make the Activity show shielded txs --- scripts/dashboard/Activity.vue | 108 +++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/scripts/dashboard/Activity.vue b/scripts/dashboard/Activity.vue index fe3431d8..35b27806 100644 --- a/scripts/dashboard/Activity.vue +++ b/scripts/dashboard/Activity.vue @@ -8,7 +8,7 @@ import { Database } from '../database.js'; import { HistoricalTx, HistoricalTxType } from '../historical_tx.js'; import { getNameOrAddress } from '../contacts-book.js'; import { getEventEmitter } from '../event_bus'; -import { beautifyNumber } from '../misc'; +import { beautifyNumber, isShieldAddress } from '../misc'; import iCheck from '../../assets/icons/icon-check.svg'; import iHourglass from '../../assets/icons/icon-hourglass.svg'; @@ -62,6 +62,39 @@ const txMap = computed(() => { }; }); +/** + * Returns the information that we need to show (icon + label + amount) for a self transaction + * @param {number} amount - The net amount of transparent PIVs in a transaction + * @param {number} shieldAmount - The net amount of shielded PIVs in a transaction + */ +function txSelfMap(amount, shieldAmount) { + if (shieldAmount == 0 || amount == 0) { + return { + icon: 'fa-recycle', + colour: 'white', + content: + shieldAmount == 0 + ? translation.activitySentTo + : 'Shield sent to self', + amount: Math.abs(shieldAmount + amount), + }; + } else if (shieldAmount > 0) { + return { + icon: 'fa-shield', + colour: 'white', + content: 'Shielding', + amount: shieldAmount, + }; + } else if (shieldAmount < 0) { + return { + icon: 'fa-shield', + colour: 'white', + content: 'De-Shielding', + amount: amount, + }; + } +} + async function update(txToAdd = 0) { const cNet = getNetwork(); // Return if wallet is not synced yet @@ -117,7 +150,7 @@ async function update(txToAdd = 0) { } // Convert to MPW's Activity format and render it - const arrTXs = wallet.toHistoricalTXs(newTxs); + const arrTXs = await wallet.toHistoricalTXs(newTxs); await parseTXs(arrTXs); txCount = found; updating.value = false; @@ -180,46 +213,31 @@ async function parseTXs(arrTXs) { blockCount - cTx.blockHeight >= (props.rewards ? cChainParams.current.coinbaseMaturity : 6); - // Choose the content type, for the Dashboard; use a generative description, otherwise, a TX-ID - // let txContent = props.rewards ? cTx.id : 'Block Reward'; - - // Format the amount to reduce text size - let formattedAmt = ''; - if (cTx.amount < 0.01) { - formattedAmt = beautifyNumber('0.01', '13px'); - } else if (cTx.amount >= 100) { - formattedAmt = beautifyNumber( - Math.round(cTx.amount).toString(), - '13px' - ); - } else { - formattedAmt = beautifyNumber(`${cTx.amount.toFixed(2)}`, '13px'); - } - - // For 'Send' TXs: Check if this is a send-to-self transaction - let fSendToSelf = false; - if (cTx.type === HistoricalTxType.SENT) { - fSendToSelf = true; - // Check all addresses to find our own, caching them for performance - for (const strAddr of cTx.receivers) { - // If a previous Tx checked this address, skip it, otherwise, check it against our own address(es) - if (!wallet.isOwnAddress(strAddr)) { - // External address, this is not a self-only Tx - fSendToSelf = false; - } - } - } + let amountToShow = Math.abs(cTx.amount + cTx.shieldAmount); // Take the icon, colour and content based on the type of the transaction let { icon, colour, content } = txMap.value[cTx.type]; const match = content.match(/{(.)}/); if (match) { let who = ''; - if (fSendToSelf) { + if (cTx.isToSelf) { who = translation.activitySelf; - } else if (cTx.shieldedOutputs) { + const descriptor = txSelfMap(cTx.amount, cTx.shieldAmount); + icon = descriptor.icon; + colour = descriptor.colour; + content = descriptor.content; + amountToShow = descriptor.amount; + } else if ( + cTx.shieldedOutputs && + cTx.type === HistoricalTxType.SENT + ) { + // We sent a shield note to someone, but we cannot decrypt the recipient + // So show a generic "Sent to shield address" who = translation.activityShieldedAddress; } else { + const shieldAddresses = cTx.receivers.filter((addr) => { + return isShieldAddress(addr); + }); const arrAddresses = cTx.receivers .map((addr) => [wallet.isOwnAddress(addr), addr]) .filter(([isOwnAddress, _]) => { @@ -231,23 +249,35 @@ async function parseTXs(arrTXs) { who = [ ...new Set( - arrAddresses.map((addr) => - addr?.length >= 32 - ? addr?.substring(0, 6) - : addr - ) + arrAddresses + .concat(shieldAddresses) + .map((addr) => + addr?.length >= 32 + ? addr?.substring(0, 6) + : addr + ) ), ].join(', ') + '...'; } content = content.replace(/{.}/, who); } + // Format the amount to reduce text size + let formattedAmt = ''; + if (amountToShow < 0.01) { + formattedAmt = '<0.01'; + } else if (amountToShow >= 100) { + formattedAmt = Math.round(amountToShow).toString(); + } else { + formattedAmt = amountToShow.toFixed(2); + } + newTxs.push({ date: strDate, id: cTx.id, content: props.rewards ? cTx.id : content, formattedAmt, - amount: cTx.amount, + amount: amountToShow, confirmed: fConfirmed, icon, colour, From 451697656db268844c8a8770a3ec35e96d558bc2 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 4 Oct 2024 14:11:52 +0200 Subject: [PATCH 06/11] Update broken tests --- tests/unit/mempool.spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/mempool.spec.js b/tests/unit/mempool.spec.js index 2294f788..f41370cc 100644 --- a/tests/unit/mempool.spec.js +++ b/tests/unit/mempool.spec.js @@ -121,20 +121,20 @@ describe('mempool tests', () => { vout: [], }); mempool.addTransaction(spendTx); - expect(mempool.getDebit(spendTx)).toBe(5000000 + 4992400); + expect(mempool.getDebit(spendTx).debit).toBe(5000000 + 4992400); - expect(mempool.getDebit(new Transaction())).toBe(0); + expect(mempool.getDebit(new Transaction()).debit).toBe(0); }); it('gives correct credit', () => { - expect(mempool.getCredit(tx)).toBe(5000000 + 4992400); + expect(mempool.getCredit(tx).credit).toBe(5000000 + 4992400); // Result should stay the same even if the UTXOs are spent mempool.setSpent(new COutpoint({ txid: tx.txid, n: 1 })); - expect(mempool.getCredit(tx)).toBe(5000000 + 4992400); + expect(mempool.getCredit(tx).credit).toBe(5000000 + 4992400); mempool.setSpent(new COutpoint({ txid: tx.txid, n: 0 })); - expect(mempool.getCredit(tx)).toBe(5000000 + 4992400); - expect(mempool.getCredit(new Transaction())).toBe(0); + expect(mempool.getCredit(tx).credit).toBe(5000000 + 4992400); + expect(mempool.getCredit(new Transaction()).credit).toBe(0); }); it('marks outpoint as spent correctly', () => { From 04796841b1fce15a0b638097b86a1446aa5fb438 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Thu, 10 Oct 2024 10:14:27 +0200 Subject: [PATCH 07/11] Add missing await --- scripts/dashboard/Activity.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/dashboard/Activity.vue b/scripts/dashboard/Activity.vue index 35b27806..aff19c55 100644 --- a/scripts/dashboard/Activity.vue +++ b/scripts/dashboard/Activity.vue @@ -129,7 +129,9 @@ async function update(txToAdd = 0) { // Only compute rewards if (!tx.isCoinStake()) continue; // Aggregate the total rewards - rewardAmount.value += wallet.toHistoricalTXs([tx])[0].amount; + rewardAmount.value += ( + await wallet.toHistoricalTXs([tx]) + )[0].amount; } // Keep track of the scan block height if (orderedTxs.length) { From c9dd54a1c211f5c09d011adfbb3b267d9243aa71 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Thu, 10 Oct 2024 11:40:47 +0200 Subject: [PATCH 08/11] Make old wallet resync if load operation is not succesful --- scripts/wallet.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/wallet.js b/scripts/wallet.js index 927302df..38b3f6b1 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -911,8 +911,7 @@ export class Wallet { if (saplingRoot !== networkSaplingRoot) { createAlert('warning', translation.badSaplingRoot, 5000); this.#mempool = new Mempool(); - // TODO: take the wallet creation height in input from users - await this.#shield.reloadFromCheckpoint(4200000); + await this.#resetShield(); await this.#transparentSync(); await this.#syncShield(); return false; @@ -946,11 +945,25 @@ export class Wallet { if (!cAccount || cAccount.shieldData == '') { return; } - this.#shield = await PIVXShield.load(cAccount.shieldData); + const loadRes = await PIVXShield.load(cAccount.shieldData); + this.#shield = loadRes.pivxShield; + // Load operation was not successful! + // Provided data are not compatible with the latest PIVX shield version. + // Resetting the shield object is required + if (!loadRes.success) { + await this.#resetShield(); + } + getEventEmitter().emit('shield-loaded-from-disk'); return; } + async #resetShield() { + // TODO: take the wallet creation height in input from users + await this.#shield.reloadFromCheckpoint(4200000); + await this.saveShieldOnDisk(); + } + /** * @returns {Promise} Number of shield satoshis of the account */ From dd370935fcffe00253803757c87bafefea56a938 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Thu, 10 Oct 2024 15:10:29 +0200 Subject: [PATCH 09/11] Add utils function to reverse and swap endianess --- scripts/utils.js | 4 ++++ scripts/wallet.js | 24 +++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts/utils.js b/scripts/utils.js index fc0fbad9..359e712d 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -13,6 +13,10 @@ export function bytesToHex(bytes) { return Buffer.from(bytes).toString('hex'); } +export function reverseAndSwapEndianess(hex) { + return bytesToHex(hexToBytes(hex).reverse()); +} + /** * Double SHA256 hash a byte array * @param {Array} buff - Bytes to hash diff --git a/scripts/wallet.js b/scripts/wallet.js index 38b3f6b1..945ec725 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -15,7 +15,13 @@ import { Database } from './database.js'; import { RECEIVE_TYPES } from './contacts-book.js'; import { Account } from './accounts.js'; import { fAdvancedMode } from './settings.js'; -import { bytesToHex, hexToBytes, sleep, startBatch } from './utils.js'; +import { + bytesToHex, + hexToBytes, + reverseAndSwapEndianess, + sleep, + startBatch, +} from './utils.js'; import { strHardwareName } from './ledger.js'; import { OutpointState, Mempool } from './mempool.js'; import { getEventEmitter } from './event_bus.js'; @@ -701,16 +707,8 @@ export class Wallet { } for (const shieldSpend of tx.shieldSpend) { - //TODO: find a better way to reverse and swap endianess - const stringArr = shieldSpend.nullifier.split('').reverse(); - for (let i = 0; i < stringArr.length - 1; i += 2) { - const temp = stringArr[i + 1]; - stringArr[i + 1] = stringArr[i]; - stringArr[i] = temp; - } - const spentNote = this.#shield.getNoteFromNullifier( - stringArr.join('') - ); + const nullifier = reverseAndSwapEndianess(shieldSpend.nullifier); + const spentNote = this.#shield.getNoteFromNullifier(nullifier); if (spentNote) { shieldDebit += spentNote.value; } @@ -904,8 +902,8 @@ export class Wallet { ); async #checkShieldSaplingRoot(networkSaplingRoot) { - const saplingRoot = bytesToHex( - hexToBytes(await this.#shield.getSaplingRoot()).reverse() + const saplingRoot = reverseAndSwapEndianess( + await this.#shield.getSaplingRoot() ); // If explorer sapling root is different from ours, there must be a sync error if (saplingRoot !== networkSaplingRoot) { From e9782c9bbd63f2b7d2fa0e8e00e6c72a72c2e2de Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 11 Oct 2024 10:31:27 +0200 Subject: [PATCH 10/11] Simplify logic and fix bug of sent to shield address --- scripts/dashboard/Activity.vue | 35 +++++++++++++++++----------------- scripts/historical_tx.js | 6 +++--- scripts/wallet.js | 5 ++--- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/scripts/dashboard/Activity.vue b/scripts/dashboard/Activity.vue index aff19c55..65969369 100644 --- a/scripts/dashboard/Activity.vue +++ b/scripts/dashboard/Activity.vue @@ -229,18 +229,8 @@ async function parseTXs(arrTXs) { colour = descriptor.colour; content = descriptor.content; amountToShow = descriptor.amount; - } else if ( - cTx.shieldedOutputs && - cTx.type === HistoricalTxType.SENT - ) { - // We sent a shield note to someone, but we cannot decrypt the recipient - // So show a generic "Sent to shield address" - who = translation.activityShieldedAddress; } else { - const shieldAddresses = cTx.receivers.filter((addr) => { - return isShieldAddress(addr); - }); - const arrAddresses = cTx.receivers + let arrAddresses = cTx.receivers .map((addr) => [wallet.isOwnAddress(addr), addr]) .filter(([isOwnAddress, _]) => { return cTx.type === HistoricalTxType.RECEIVED @@ -248,18 +238,27 @@ async function parseTXs(arrTXs) { : !isOwnAddress; }) .map(([_, addr]) => getNameOrAddress(cAccount, addr)); + if (cTx.type == HistoricalTxType.RECEIVED) { + arrAddresses = arrAddresses.concat(cTx.shieldReceivers); + } who = [ ...new Set( - arrAddresses - .concat(shieldAddresses) - .map((addr) => - addr?.length >= 32 - ? addr?.substring(0, 6) - : addr - ) + arrAddresses.map((addr) => + addr?.length >= 32 + ? addr?.substring(0, 6) + : addr + ) ), ].join(', ') + '...'; + if ( + cTx.type == HistoricalTxType.SENT && + arrAddresses.length == 0 + ) { + // We sent a shield note to someone, but we cannot decrypt the recipient + // So show a generic "Sent to shield address" + who = translation.activityShieldedAddress; + } } content = content.replace(/{.}/, who); } diff --git a/scripts/historical_tx.js b/scripts/historical_tx.js index 2e149be7..6846423a 100644 --- a/scripts/historical_tx.js +++ b/scripts/historical_tx.js @@ -6,7 +6,7 @@ export class HistoricalTx { * @param {HistoricalTxType} type - The type of transaction. * @param {string} id - The transaction ID. * @param {Array} receivers - The list of 'output addresses'. - * @param {boolean} shieldedOutputs - If this transaction contains Shield outputs. + * @param {Array} shieldReceivers - The list of decrypted 'shield output addresses'. * @param {number} time - The block time of the transaction. * @param {number} blockHeight - The block height of the transaction. * @param {number} amount - The transparent amount transacted, in coins. @@ -17,7 +17,7 @@ export class HistoricalTx { type, id, receivers, - shieldedOutputs, + shieldReceivers, time, blockHeight, amount, @@ -27,7 +27,7 @@ export class HistoricalTx { this.type = type; this.id = id; this.receivers = receivers; - this.shieldedOutputs = shieldedOutputs; + this.shieldReceivers = shieldReceivers; this.time = time; this.blockHeight = blockHeight; this.amount = amount; diff --git a/scripts/wallet.js b/scripts/wallet.js index 945ec725..550585a6 100644 --- a/scripts/wallet.js +++ b/scripts/wallet.js @@ -642,8 +642,7 @@ export class Wallet { const nShieldAmount = (shieldCredit - shieldDebit) / COIN; const ownAllShield = shieldDebit - shieldCredit === tx.valueBalance; // The receiver addresses, if any - let arrReceivers = - this.getOutAddress(tx).concat(arrShieldReceivers); + let arrReceivers = this.getOutAddress(tx); const getFilteredCredit = (filter) => { return tx.vout .filter((_, i) => { @@ -682,7 +681,7 @@ export class Wallet { type, tx.txid, arrReceivers, - tx.hasShieldData, + arrShieldReceivers, tx.blockTime, tx.blockHeight, nAmount, From 7ae5fb88226881e82c6de0e961d2a7bfdcfb7622 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 11 Oct 2024 10:33:50 +0200 Subject: [PATCH 11/11] Remove unused imports --- scripts/dashboard/Activity.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/dashboard/Activity.vue b/scripts/dashboard/Activity.vue index 65969369..d1283453 100644 --- a/scripts/dashboard/Activity.vue +++ b/scripts/dashboard/Activity.vue @@ -2,17 +2,16 @@ import { ref, computed, watch, onMounted } from 'vue'; import { getNetwork } from '../network.js'; import { wallet } from '../wallet.js'; -import { COIN, cChainParams } from '../chain_params.js'; +import { cChainParams } from '../chain_params.js'; 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 { beautifyNumber, isShieldAddress } from '../misc'; import iCheck from '../../assets/icons/icon-check.svg'; import iHourglass from '../../assets/icons/icon-hourglass.svg'; -import { blockCount, optimiseCurrencyLocale } from '../global.js'; +import { blockCount } from '../global.js'; const props = defineProps({ title: String,