Skip to content

Commit

Permalink
Merge branch 'master' into 1.5.0_bump
Browse files Browse the repository at this point in the history
  • Loading branch information
Duddino authored Nov 14, 2023
2 parents d93e68c + c1ac0e7 commit 2c7a78c
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 51 deletions.
12 changes: 8 additions & 4 deletions scripts/dashboard/AccessWallet.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import coinPlant from '../../assets/coin_plant.svg';
import pLogo from '../../assets/p_logo.svg';
import { ref, watch } from 'vue';
import { ref, watch, toRefs } from 'vue';
import { translation } from '../i18n.js';
import { isBase64 } from '../misc';
Expand All @@ -13,7 +13,7 @@ const passwordPlaceholder = ref(translation.password);
const props = defineProps({
advancedMode: Boolean,
});
const advancedMode = ref(props.advancedMode);
const { advancedMode } = toRefs(props);
/**
* Secret is the thing being imported:
Expand All @@ -26,12 +26,12 @@ const secret = ref('');
*/
const password = ref('');
watch(secret, (secret) => {
watch([secret, advancedMode], ([secret, advancedMode]) => {
// If it cointains spaces, it's likely a bip39 seed
const fContainsSpaces = secret.trim().includes(' ');
// Show password input if it's a bip39 seed and we're in advanced mode
if (fContainsSpaces && advancedMode.value) {
if (fContainsSpaces && advancedMode) {
showPassword.value = true;
}
// If it's a Base64 secret, it's likely an MPW encrypted import,
Expand All @@ -48,6 +48,10 @@ watch(secret, (secret) => {
? translation.optionalPassphrase
: translation.password;
});
watch(showPassword, (showPassword) => {
// Empty password prompt when hidden
if (!showPassword) password.value = '';
});
const emit = defineEmits(['import-wallet']);
function importWallet() {
emit('import-wallet', secret.value, password.value);
Expand Down
15 changes: 13 additions & 2 deletions scripts/dashboard/Activity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ref, computed, watch, onMounted } from 'vue';
import { getNetwork } from '../network.js';
import { wallet } from '../wallet.js';
import { mempool } from '../global.js';
import { cChainParams } from '../chain_params.js';
import { COIN, cChainParams } from '../chain_params.js';
import { translation } from '../i18n.js';
import { Database } from '../database.js';
import { HistoricalTx, HistoricalTxType } from '../mempool';
Expand All @@ -19,7 +19,10 @@ const txs = ref([]);
let txCount = 0;
const updating = ref(false);
const isHistorySynced = ref(false);
const rewardsText = ref('-');
const rewardsText = computed(
() => `${isHistorySynced.value ? '' : ''}${rewardAmount.value.toFixed(2)}`
);
const rewardAmount = ref(0);
const ticker = computed(() => cChainParams.current.TICKER);
const explorerUrl = ref(getNetwork()?.strUrl);
const txMap = computed(() => {
Expand Down Expand Up @@ -220,6 +223,7 @@ async function parseTXs(arrTXs) {
id: cTx.id,
content: props.rewards ? cTx.id : content,
formattedAmt,
amount: cTx.amount,
confirmed: fConfirmed,
icon,
colour,
Expand All @@ -228,6 +232,13 @@ async function parseTXs(arrTXs) {
txs.value = newTxs;
}
if (props.rewards) {
watch(
txs,
(txs) =>
(rewardAmount.value = txs.reduce((acc, tx) => acc + tx.amount, 0))
);
}
function reset() {
txs.value = [];
Expand Down
15 changes: 9 additions & 6 deletions scripts/dashboard/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ watch(showExportModal, async (showExportModal) => {
keyToBackup.value = '';
}
});
getEventEmitter().on(
'advanced-mode',
(fAdvancedMode) => (advancedMode.value = fAdvancedMode)
);
getEventEmitter().on('advanced-mode', (fAdvancedMode) => {
advancedMode.value = fAdvancedMode;
});
/**
* Parses whatever the secret is to a MasterKey
Expand Down Expand Up @@ -118,7 +117,7 @@ async function parseSecret(secret, password = '') {
);
if (!ok) throw new Error(msg);
return new HdMasterKey({
seed: await mnemonicToSeed(phrase),
seed: await mnemonicToSeed(phrase, password),
});
},
},
Expand Down Expand Up @@ -491,7 +490,11 @@ defineExpose({
<template>
<div id="keypair" class="tabcontent">
<div class="row m-0">
<Login v-show="!isImported" @import-wallet="importWallet" />
<Login
v-show="!isImported"
:advancedMode="advancedMode"
@import-wallet="importWallet"
/>
<br />
Expand Down
7 changes: 7 additions & 0 deletions scripts/dashboard/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import pLogo from '../../assets/p_logo.svg';
import VanityGen from './VanityGen.vue';
import CreateWallet from './CreateWallet.vue';
import AccessWallet from './AccessWallet.vue';
import { watch, toRefs } from 'vue';
defineEmits(['import-wallet']);
const props = defineProps({
advancedMode: Boolean,
});
const { advancedMode } = toRefs(props);
</script>

<template>
Expand Down Expand Up @@ -67,6 +73,7 @@ defineEmits(['import-wallet']);

<br />
<AccessWallet
:advancedMode="advancedMode"
@import-wallet="
(secret, password) =>
$emit('import-wallet', { type: 'hd', secret, password })
Expand Down
8 changes: 5 additions & 3 deletions scripts/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -972,14 +972,16 @@ export async function sweepAddress(arrUTXOs, sweepingMasterKey, nFixedFee = 0) {
const nFee = nFixedFee || getNetwork().getFee(cTx.serialize().length);

// Use a new address from our wallet to sweep the UTXOs in to
const strAddress = (await getNewAddress(true, false))[0];
const strAddress = (
await getNewAddress({ updateGUI: true, verify: false, nReceiving: 1 })
)[0];

// Sweep the full funds amount, minus the fee, leaving no change from any sweeped UTXOs
cTx.addoutput(strAddress, (nTotal - nFee) / COIN);

// Sign using the given Master Key, then broadcast the sweep, returning the TXID (or a failure)
const sweepingWallet = new Wallet(0, false);
await sweepingWallet.setMasterKey(sweepingMasterKey);
sweepingWallet.setMasterKey(sweepingMasterKey);

const sign = await signTransaction(cTx, sweepingWallet);
return await getNetwork().sendTransaction(sign);
Expand Down Expand Up @@ -1963,7 +1965,7 @@ export async function createProposal() {
// If Advanced Mode is enabled and an address is given, use the provided address, otherwise, generate a new one
const strAddress =
document.getElementById('proposalAddress').value.trim() ||
(await wallet.getNewAddress())[0];
wallet.getNewAddress(1)[0];
const nextSuperblock = await Masternode.getNextSuperblock();
const proposal = {
name: strTitle,
Expand Down
7 changes: 4 additions & 3 deletions scripts/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export async function undelegateGUI() {
if (!validateAmount(nAmount)) return;

// Generate a new address to undelegate towards
const [address] = wallet.getNewAddress();
const [address] = wallet.getNewAddress(1);

// Perform the TX
const cTxRes = await createAndSendTransaction({
Expand Down Expand Up @@ -304,6 +304,7 @@ export async function createAndSendTransaction({
const nChange = cCoinControl.nValue - (nFee + amount);
const [changeAddress, _] = await getNewAddress({
verify: wallet.isHardwareWallet(),
nReceiving: 1,
});

/**
Expand Down Expand Up @@ -342,8 +343,7 @@ export async function createAndSendTransaction({
// For custom Cold Owner Addresses, it could be an external address, so we need the mempool to class it as an 'external send'
const strOwnerAddress = fCustomColdOwner
? delegationOwnerAddress
: (await wallet.getNewAddress())[0];
const strOwnerPath = await wallet.isOwnAddress(strOwnerAddress);
: wallet.getNewAddress(1)[0];

// Create the Delegation output
cTx.addcoldstakingoutput(strOwnerAddress, address, amount / COIN);
Expand Down Expand Up @@ -425,6 +425,7 @@ export async function createMasternode() {
// Generate the Masternode collateral
const [address] = await getNewAddress({
verify: wallet.isHardwareWallet(),
nReceiving: 1,
});
const result = await createAndSendTransaction({
amount: cChainParams.current.collateralInSats,
Expand Down
100 changes: 67 additions & 33 deletions scripts/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ import {
* in future PRs this class will manage balance, UTXOs, masternode etc...
*/
export class Wallet {
/**
* We are using two chains: The external chain, and the internal one (i.e. change addresses)
* See https://github.com/bitcoin/bips/blob/master/bip-0048.mediawiki for more info
* (Change paragraph)
*/
static chains = 2;
/**
* @type {import('./masterkey.js').MasterKey}
*/
Expand All @@ -40,20 +46,23 @@ export class Wallet {
* @type {number}
*/
#nAccount;

/**
* Map bip48 change -> Loaded index
* Number of loaded indexes, loaded means that they are in the ownAddresses map
* @type {number}
* @type {Map<number, number>}
*/
#loadedIndexes = 0;
#loadedIndexes = new Map();
/**
* Map bip48 change -> Highest used index
* Highest index used, where used means that the corresponding address is on chain (for example in a tx)
* @type {number}
* @type {Map<number, number>}
*/
#highestUsedIndex = 0;
#highestUsedIndices = new Map();
/**
* @type {number}
* @type {Map<number, number>}
*/
#addressIndex = 0;
#addressIndices = new Map();
/**
* Map our own address -> Path
* @type {Map<String, String?>}
Expand All @@ -78,6 +87,10 @@ export class Wallet {
this.#nAccount = nAccount;
this.#isMainWallet = isMainWallet;
this.#lockedCoins = new Set();
for (let i = 0; i < Wallet.chains; i++) {
this.#highestUsedIndices.set(i, 0);
this.#loadedIndexes.set(i, 0);
}
}

/**
Expand Down Expand Up @@ -185,16 +198,22 @@ export class Wallet {
if (this.#isMainWallet) {
getNetwork().setWallet(this);
}
this.loadAddresses();
for (let i = 0; i < Wallet.chains; i++) this.loadAddresses(i);
}

/**
* Reset the wallet, indexes address map and so on
*/
reset() {
this.#highestUsedIndex = 0;
this.#loadedIndexes = 0;
this.#highestUsedIndices = new Map();
this.#loadedIndexes = new Map();
this.#ownAddresses = new Map();
this.#addressIndices = new Map();
for (let i = 0; i < Wallet.chains; i++) {
this.#highestUsedIndices.set(i, 0);
this.#loadedIndexes.set(i, 0);
this.#addressIndices.set(i, 0);
}
// TODO: This needs to be refactored
// The wallet could own its own mempool and network?
// Instead of having this isMainWallet flag
Expand All @@ -210,7 +229,7 @@ export class Wallet {
*
*/
getCurrentAddress() {
return this.getAddress(0, this.#addressIndex);
return this.getAddress(0, this.#addressIndices.get(0));
}

/**
Expand Down Expand Up @@ -302,16 +321,26 @@ export class Wallet {
/**
* @return [string, string] Address and its BIP32 derivation path
*/
getNewAddress() {
const last = this.#highestUsedIndex;
this.#addressIndex =
(this.#addressIndex > last ? this.#addressIndex : last) + 1;
if (this.#addressIndex - last > MAX_ACCOUNT_GAP) {
getNewAddress(nReceiving = 0) {
const last = this.#highestUsedIndices.get(nReceiving);
this.#addressIndices.set(
nReceiving,
(this.#addressIndices.get(nReceiving) > last
? this.#addressIndices.get(nReceiving)
: last) + 1
);
if (this.#addressIndices.get(nReceiving) - last > MAX_ACCOUNT_GAP) {
// If the user creates more than ${MAX_ACCOUNT_GAP} empty wallets we will not be able to sync them!
this.#addressIndex = last;
this.#addressIndices.set(nReceiving, last);
}
const path = this.getDerivationPath(0, this.#addressIndex);
const address = this.getAddress(0, this.#addressIndex);
const path = this.getDerivationPath(
nReceiving,
this.#addressIndices.get(nReceiving)
);
const address = this.getAddress(
nReceiving,
this.#addressIndices.get(nReceiving)
);
return [address, path];
}

Expand All @@ -332,34 +361,38 @@ export class Wallet {
);
const path = this.isOwnAddress(address);
if (path) {
this.#highestUsedIndex = Math.max(
parseInt(path.split('/')[5]),
this.#highestUsedIndex
const nReceiving = parseInt(path.split('/')[4]);
this.#highestUsedIndices.set(
nReceiving,
Math.max(
parseInt(path.split('/')[5]),
this.#highestUsedIndices.get(nReceiving)
)
);
if (
this.#highestUsedIndex + MAX_ACCOUNT_GAP >=
this.#loadedIndexes
this.#highestUsedIndices.get(nReceiving) + MAX_ACCOUNT_GAP >=
this.#loadedIndexes.get(nReceiving)
) {
this.loadAddresses();
this.loadAddresses(nReceiving);
}
}
}

/**
* Load MAX_ACCOUNT_GAP inside #ownAddresses map.
* @param {number} chain - Chain to load
*/
loadAddresses() {
loadAddresses(chain) {
if (this.isHD()) {
for (
let i = this.#loadedIndexes;
i <= this.#loadedIndexes + MAX_ACCOUNT_GAP;
i++
) {
const path = this.getDerivationPath(0, i);
const start = this.#loadedIndexes.get(chain);
const end = start + MAX_ACCOUNT_GAP;
for (let i = start; i <= end; i++) {
const path = this.getDerivationPath(chain, i);
const address = this.#masterKey.getAddress(path);
this.#ownAddresses.set(address, path);
}
this.#loadedIndexes += MAX_ACCOUNT_GAP;

this.#loadedIndexes.set(chain, end);
} else {
this.#ownAddresses.set(this.getKeyToExport(), ':)');
}
Expand Down Expand Up @@ -694,8 +727,9 @@ export async function hasEncryptedWallet() {
export async function getNewAddress({
updateGUI = false,
verify = false,
nReceiving = 0,
} = {}) {
const [address, path] = wallet.getNewAddress();
const [address, path] = wallet.getNewAddress(nReceiving);
if (verify && wallet.isHardwareWallet()) {
// Generate address to present to the user without asking to verify
const confAddress = await confirmPopup({
Expand Down

0 comments on commit 2c7a78c

Please sign in to comment.