Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shield Activity #416

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
108 changes: 69 additions & 39 deletions scripts/dashboard/Activity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 } 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,
Expand Down Expand Up @@ -62,6 +61,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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow the design colors/icons

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
Expand Down Expand Up @@ -96,7 +128,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) {
Expand All @@ -117,7 +151,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;
Expand Down Expand Up @@ -180,54 +214,32 @@ 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) {
who = translation.activityShieldedAddress;
const descriptor = txSelfMap(cTx.amount, cTx.shieldAmount);
icon = descriptor.icon;
colour = descriptor.colour;
content = descriptor.content;
amountToShow = descriptor.amount;
} else {
const arrAddresses = cTx.receivers
let arrAddresses = cTx.receivers
.map((addr) => [wallet.isOwnAddress(addr), addr])
.filter(([isOwnAddress, _]) => {
return cTx.type === HistoricalTxType.RECEIVED
? isOwnAddress
: !isOwnAddress;
})
.map(([_, addr]) => getNameOrAddress(cAccount, addr));
if (cTx.type == HistoricalTxType.RECEIVED) {
arrAddresses = arrAddresses.concat(cTx.shieldReceivers);
}
who =
[
...new Set(
Expand All @@ -238,16 +250,34 @@ async function parseTXs(arrTXs) {
)
),
].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);
}

// 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,
Expand Down
16 changes: 11 additions & 5 deletions scripts/historical_tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,33 @@ export class HistoricalTx {
* @param {HistoricalTxType} type - The type of transaction.
* @param {string} id - The transaction ID.
* @param {Array<string>} receivers - The list of 'output addresses'.
* @param {boolean} shieldedOutputs - If this transaction contains Shield outputs.
* @param {Array<string>} 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 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,
id,
receivers,
shieldedOutputs,
shieldReceivers,
time,
blockHeight,
amount
amount,
shieldAmount,
isToSelf
) {
this.type = type;
this.id = id;
this.receivers = receivers;
this.shieldedOutputs = shieldedOutputs;
this.shieldReceivers = shieldReceivers;
this.time = time;
this.blockHeight = blockHeight;
this.amount = amount;
this.shieldAmount = shieldAmount;
this.isToSelf = isToSelf;
}
}

Expand Down
38 changes: 22 additions & 16 deletions scripts/mempool.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}

/**
Expand All @@ -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,
};
}

/**
Expand Down
4 changes: 4 additions & 0 deletions scripts/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<number>} buff - Bytes to hash
Expand Down
Loading
Loading