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

Add ledger persistance #381

Merged
merged 4 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions scripts/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Account {
* @param {String} [accountData.coldAddress] - The Cold Address that this account delegates to.
* @param {String} [accountData.shieldData] - Shield data necessary to load shielding
* @param {String} [accountData.encExtsk] - Encrypted extended spending key
* @param {boolean} [accountData.isHardware] - Whether this is a hardware wallet
*/
constructor(accountData) {
// Keys take the Constructor as priority, but if missing, default to their "Type" in empty form for type-safety
Expand All @@ -24,6 +25,7 @@ export class Account {
this.coldAddress = accountData?.coldAddress || '';
this.shieldData = accountData?.shieldData || '';
this.encExtsk = accountData?.encExtsk || '';
this.isHardware = accountData?.isHardware || false;
}

/** @type {String} The public key. */
Expand All @@ -50,6 +52,9 @@ export class Account {
/** @type {String} Encrypted extended spending key*/
encExtsk = '';

/** @type{boolean} whether this is a hardware/ledger account */
isHardware = false;

/**
* Search for a Contact in this account, by specific properties
* @param {Object} settings
Expand Down
4 changes: 3 additions & 1 deletion scripts/composables/use_wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ export const useWallet = defineStore('wallet', () => {
const isViewOnly = ref(wallet.isViewOnly());
const isSynced = ref(wallet.isSynced);
const getKeyToBackup = async () => await wallet.getKeyToBackup();
const getKeyToExport = () => wallet.getKeyToExport();
const isEncrypted = ref(true);
const loadFromDisk = () => wallet.loadFromDisk();
const hasShield = ref(wallet.hasShield());

const setMasterKey = async ({ mk, extsk }) => {
wallet.setMasterKey({ mk, extsk });
await wallet.setMasterKey({ mk, extsk });
isImported.value = wallet.isLoaded();
isHardwareWallet.value = wallet.isHardwareWallet();
isHD.value = wallet.isHD();
Expand Down Expand Up @@ -120,6 +121,7 @@ export const useWallet = defineStore('wallet', () => {
isEncrypted,
isSynced,
getKeyToBackup,
getKeyToExport,
setMasterKey,
setExtsk,
setShield,
Expand Down
37 changes: 29 additions & 8 deletions scripts/dashboard/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import pShieldLogo from '../../assets/icons/icon_shield_pivx.svg';
import pIconCamera from '../../assets/icons/icon-camera.svg';
import { ParsedSecret } from '../parsed_secret.js';
import { storeToRefs } from 'pinia';
import { Account } from '../accounts';

const wallet = useWallet();
const activity = ref(null);
Expand Down Expand Up @@ -91,7 +92,11 @@ async function importWallet({ type, secret, password = '' }) {
);
return false;
}
parsedSecret = new ParsedSecret(await HardwareWalletMasterKey.create());
parsedSecret = new ParsedSecret(
secret
? HardwareWalletMasterKey.fromXPub(secret)
: await HardwareWalletMasterKey.create()
);

createAlert(
'info',
Expand All @@ -112,6 +117,19 @@ async function importWallet({ type, secret, password = '' }) {
wallet.setShield(parsedSecret.shield);

if (needsToEncrypt.value) showEncryptModal.value = true;
if (wallet.isHardwareWallet) {
// Save the xpub without needing encryption if it's ledger
const database = await Database.getInstance();
const account = new Account({
publicKey: wallet.getKeyToExport(),
isHardware: true,
});
if (await database.getAccount()) {
await database.updateAccount(account);
} else {
await database.addAccount(account);
}
}

// Start syncing in the background
wallet.sync().then(() => {
Expand Down Expand Up @@ -361,28 +379,31 @@ function getMaxBalance(useShieldInputs) {
transferAmount.value = (coinSatoshi / COIN).toString();
}

getEventEmitter().on('toggle-network', async () => {
async function importFromDatabase() {
const database = await Database.getInstance();
const account = await database.getAccount();
await wallet.setMasterKey({ mk: null });
activity.value?.reset();

if (wallet.isEncrypted) {
if (account?.isHardware) {
await importWallet({ type: 'hardware', secret: account.publicKey });
} else if (wallet.isEncrypted) {
await importWallet({ type: 'hd', secret: account.publicKey });
}

updateLogOutButton();
}

getEventEmitter().on('toggle-network', async () => {
importFromDatabase();
// TODO: When tab component is written, simply emit an event
doms.domDashboard.click();
});

onMounted(async () => {
await start();
await importFromDatabase();

if (wallet.isEncrypted) {
const database = await Database.getInstance();
const { publicKey } = await database.getAccount();
await importWallet({ type: 'hd', secret: publicKey });

const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('addcontact')) {
await handleContactRequest(urlParams);
Expand Down
25 changes: 16 additions & 9 deletions scripts/ledger.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,28 @@ let transport;
*/
export let cHardwareWallet = null;
export let strHardwareName = '';

/**
* Setup ledger connection. Must be called at the beginning of each ledger function
*/
async function setupConnection() {
// Check if we haven't setup a connection yet OR the previous connection disconnected
if (!cHardwareWallet || transport._disconnectEmitted) {
const AppBtc = (await import('@ledgerhq/hw-app-btc')).default;
const TransportWebUSB = (await import('@ledgerhq/hw-transport-webusb'))
.default;
transport = await TransportWebUSB.create();
cHardwareWallet = new AppBtc({ transport, currency: 'PIVX' });
}
}
/**
* Get hardware wallet keys.
* @param {string} path - bip32 path to the key
* @returns {Promise<string?>}
*/
export async function getHardwareWalletKeys(path, xpub = false, verify = true) {
try {
// Check if we haven't setup a connection yet OR the previous connection disconnected
if (!cHardwareWallet || transport._disconnectEmitted) {
const AppBtc = (await import('@ledgerhq/hw-app-btc')).default;
const TransportWebUSB = (
await import('@ledgerhq/hw-transport-webusb')
).default;
transport = await TransportWebUSB.create();
cHardwareWallet = new AppBtc({ transport, currency: 'PIVX' });
}
await setupConnection();

// Update device info and fetch the pubkey
strHardwareName =
Expand Down Expand Up @@ -140,6 +146,7 @@ export async function getHardwareWalletKeys(path, xpub = false, verify = true) {
* @param {import('./transaction.js').Transaction} transaction - tx to sign
*/
export async function ledgerSignTransaction(wallet, transaction) {
await setupConnection();
const ledgerTx = cHardwareWallet.splitTransaction(transaction.serialize());
const outputs = transaction.vout.map((o) => {
const { addresses, type } = wallet.getAddressesFromScript(o.script);
Expand Down
11 changes: 10 additions & 1 deletion scripts/masterkey.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,16 @@ export class HardwareWalletMasterKey extends HdMasterKey {
const xpub = await getHardwareWalletKeys(path, true, false);
if (!xpub) throw new Error('Failed to get hardware wallet keys.');
HardwareWalletMasterKey.#initializing = true;
return new HardwareWalletMasterKey(xpub);
const mk = new HardwareWalletMasterKey(xpub);
HardwareWalletMasterKey.#initializing = false;
return mk;
}

static fromXPub(xpub) {
HardwareWalletMasterKey.#initializing = true;
const mk = new HardwareWalletMasterKey(xpub);
HardwareWalletMasterKey.#initializing = false;
return mk;
}

async getPublicKey(path, { verify } = {}) {
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/use_wallet.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('useWallet tests', () => {
});

it('is synced after importing key', async () => {
walletComposable.setMasterKey({
await walletComposable.setMasterKey({
mk: new LegacyMasterKey({
pkBytes: new Uint8Array([
181, 66, 141, 90, 213, 58, 137, 158, 160, 57, 109, 252, 51,
Expand Down
Loading