You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

195 lines
6.1 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="Object.keys(didArrays).length > 1">
Choose existing DIDs from same seed phrase to compute derivation.
</p>
<ul class="mb-4">
<li
v-for="dids in Object.values(didArrays)"
:key="dids[0].did"
class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2"
@click="switchAccount(dids[0].did)"
>
<font-awesome
v-if="dids[0].did == 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">
<code>{{ dids.map((d) => d.did).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 * as R from "ramda";
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 {
retrieveAllAccountsMetadata,
retrieveFullyDecryptedAccount,
saveNewIdentity,
} from "../libs/util";
import { logger } from "../utils/logger";
import { Account, AccountEncrypted } from "../db/tables/accounts";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
NOTIFY_ACCOUNT_DERIVATION_SUCCESS,
NOTIFY_ACCOUNT_DERIVATION_ERROR,
} from "@/constants/notifications";
@Component({
components: {},
mixins: [PlatformServiceMixin],
})
export default class ImportAccountView extends Vue {
$route!: RouteLocationNormalizedLoaded;
$router!: Router;
$notify!: (notification: any, timeout?: number) => void;
notify!: ReturnType<typeof createNotifyHelpers>;
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
didArrays: Record<string, Account[]> = {};
selectedArrayFirstDid = "";
created() {
this.notify = createNotifyHelpers(this.$notify);
}
async mounted() {
const accounts: AccountEncrypted[] = await retrieveAllAccountsMetadata();
const decryptedAccounts: (Account | undefined)[] = await Promise.all(
accounts.map(async (account) => {
return retrieveFullyDecryptedAccount(account.did);
}),
);
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter(
(account) => account !== undefined,
);
// group by account.mnemonic
const groupedAccounts: Record<string, Account[]> = R.groupBy(
(a) => a.mnemonic || "",
filteredDecryptedAccounts,
) as Record<string, Account[]>;
this.didArrays = groupedAccounts;
if (Object.keys(this.didArrays).length > 0) {
this.selectedArrayFirstDid = Object.values(this.didArrays)[0][0].did;
}
}
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<Account> =
Object.values(this.didArrays).find(
(dids) => dids[0].did === this.selectedArrayFirstDid,
) || [];
// extract the derivationPath array and sort it
const derivationPaths = selectedArray.map(
(account) => account.derivationPath,
);
derivationPaths.sort((a, b) => {
const aParts = a?.split("/");
const aLast = aParts?.[aParts.length - 1];
const bParts = b?.split("/");
const bLast = bParts?.[bParts.length - 1];
return parseInt(aLast || "0") - parseInt(bLast || "0");
});
// we're sure there's at least one
const maxDerivPath: string = derivationPaths[
derivationPaths.length - 1
] as string;
const newDerivPath = nextDerivationPath(maxDerivPath);
const mne = selectedArray[0].mnemonic as string;
const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath);
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath);
try {
await saveNewIdentity(newId, mne, newDerivPath);
// record that as the active DID
await this.$saveSettings({ activeDid: newId.did });
await this.$saveUserSettings(newId.did, {
isRegistered: false,
});
this.notify.success(
NOTIFY_ACCOUNT_DERIVATION_SUCCESS.message,
TIMEOUTS.STANDARD,
);
this.$router.push({ name: "account" });
} catch (err) {
logger.error("Error saving mnemonic & updating settings:", err);
this.notify.error(NOTIFY_ACCOUNT_DERIVATION_ERROR.message, TIMEOUTS.LONG);
}
}
}
</script>