208 lines
6.9 KiB

<template>
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Breadcrumb -->
<div id="ViewBreadcrumb" class="mb-8">
<h1 class="text-lg text-center font-light relative px-7">
<!-- Cancel -->
<button
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
@click="$router.go(-1)"
>
<font-awesome icon="chevron-left"></font-awesome>
</button>
Derive from Existing Identity
</h1>
</div>
<!-- Import Account Form -->
<div>
<p class="text-center text-xl mb-4 font-light">
Will increment the maximum known derivation path from the existing seed.
</p>
<p v-if="didArrays.length > 1">
Choose existing DIDs from same seed phrase to compute derivation.
</p>
<ul class="mb-4">
<li
v-for="dids in didArrays"
:key="dids[0]"
class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2"
@click="switchAccount(dids[0])"
>
<font-awesome
v-if="dids[0] == selectedArrayFirstDid"
icon="circle"
class="fa-fw text-blue-500 text-xl mr-3"
></font-awesome>
<font-awesome
v-else
icon="circle"
class="fa-fw text-slate-400 text-xl mr-3"
></font-awesome>
<span class="overflow-hidden">
<div class="text-sm text-slate-500 truncate">
<code>{{ dids.join(",") }}</code>
</div>
</span>
</li>
</ul>
</div>
<div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
<button
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
@click="incrementDerivation()"
>
Increment and Import
</button>
<button
type="button"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
@click="onCancelClick()"
>
Cancel
</button>
</div>
</div>
</section>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { Router, RouteLocationNormalizedLoaded } from "vue-router";
import {
DEFAULT_ROOT_DERIVATION_PATH,
deriveAddress,
newIdentifier,
nextDerivationPath,
} from "../libs/crypto";
import { accountsDBPromise, db } from "../db/index";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import * as databaseUtil from "../db/databaseUtil";
import { retrieveAllAccountsMetadata } from "../libs/util";
import { logger } from "../utils/logger";
import { Account } from "../db/tables/accounts";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { USE_DEXIE_DB } from "@/constants/app";
@Component({
components: {},
})
export default class ImportAccountView extends Vue {
$route!: RouteLocationNormalizedLoaded;
$router!: Router;
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
didArrays: Array<Array<string>> = [];
selectedArrayFirstDid = "";
async mounted() {
const accounts: Account[] = await retrieveAllAccountsMetadata();
const seedDids: Record<string, Array<string>> = {};
accounts.forEach((account) => {
// Since we're only getting metadata, we can't check mnemonic
// Instead, we'll group by derivation path
if (account.derivationPath) {
const prevDids: Array<string> = seedDids[account.derivationPath] || [];
seedDids[account.derivationPath] = prevDids.concat([account.did]);
}
});
this.didArrays = Object.values(seedDids);
if (this.didArrays.length > 0) {
this.selectedArrayFirstDid = this.didArrays[0][0];
}
}
public onCancelClick() {
this.$router.back();
}
public switchAccount(did: string) {
this.selectedArrayFirstDid = did;
}
public async incrementDerivation() {
// find the maximum derivation path for the selected DIDs
const selectedArray: Array<string> =
this.didArrays.find((dids) => dids[0] === this.selectedArrayFirstDid) ||
[];
const platformService = PlatformServiceFactory.getInstance();
const qmarks = selectedArray.map(() => "?").join(",");
const queryResult = await platformService.dbQuery(
`SELECT * FROM accounts WHERE did IN (${qmarks})`,
selectedArray,
);
let allMatchingAccounts = databaseUtil.mapQueryResultToValues(
queryResult,
) as unknown as Account[];
if (USE_DEXIE_DB) {
const accountsDB = await accountsDBPromise; // let's match derived accounts differently so we don't need the private info
allMatchingAccounts = (await accountsDB.accounts
.where("did")
.anyOf(...selectedArray)
.toArray()) as Account[];
}
const accountWithMaxDeriv = allMatchingAccounts[0];
allMatchingAccounts.slice(1).forEach((account) => {
if (
account.derivationPath &&
accountWithMaxDeriv.derivationPath &&
account.derivationPath > accountWithMaxDeriv.derivationPath
) {
accountWithMaxDeriv.derivationPath = account.derivationPath;
}
});
// increment the last number in that max derivation path
const newDerivPath = nextDerivationPath(
accountWithMaxDeriv.derivationPath as string,
);
const mne: string = accountWithMaxDeriv.mnemonic as string;
const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath);
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath);
try {
const { sql, params } = databaseUtil.generateInsertStatement(
{
dateCreated: new Date().toISOString(),
derivationPath: newDerivPath,
did: newId.did,
identity: JSON.stringify(newId),
mnemonic: mne,
publicKeyHex: newId.keys[0].publicKeyHex,
},
"accounts",
);
const platformService = PlatformServiceFactory.getInstance();
await platformService.dbExec(sql, params);
if (USE_DEXIE_DB) {
const accountsDB = await accountsDBPromise;
await accountsDB.accounts.add({
dateCreated: new Date().toISOString(),
derivationPath: newDerivPath,
did: newId.did,
identity: JSON.stringify(newId),
mnemonic: mne,
publicKeyHex: newId.keys[0].publicKeyHex,
});
}
// record that as the active DID
await platformService.dbExec("UPDATE settings SET activeDid = ?", [
newId.did,
]);
if (USE_DEXIE_DB) {
await db.settings.update(MASTER_SETTINGS_KEY, {
activeDid: newId.did,
});
}
this.$router.push({ name: "account" });
} catch (err) {
logger.error("Error saving mnemonic & updating settings:", err);
}
}
}
</script>