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
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>
|
|
|