forked from trent_larson/crowd-funder-for-time-pwa
fix import for derived accounts and hopefully make other account-access code more robust
This commit is contained in:
@@ -20,18 +20,18 @@
|
||||
Will increment the maximum known derivation path from the existing seed.
|
||||
</p>
|
||||
|
||||
<p v-if="didArrays.length > 1">
|
||||
<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 didArrays"
|
||||
:key="dids[0]"
|
||||
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])"
|
||||
@click="switchAccount(dids[0].did)"
|
||||
>
|
||||
<font-awesome
|
||||
v-if="dids[0] == selectedArrayFirstDid"
|
||||
v-if="dids[0].did == selectedArrayFirstDid"
|
||||
icon="circle"
|
||||
class="fa-fw text-blue-500 text-xl mr-3"
|
||||
></font-awesome>
|
||||
@@ -41,8 +41,8 @@
|
||||
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 class="text-sm text-slate-500">
|
||||
<code>{{ dids.map((d) => d.did).join(" ") }}</code>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
@@ -69,6 +69,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import * as R from "ramda";
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router, RouteLocationNormalizedLoaded } from "vue-router";
|
||||
|
||||
@@ -80,12 +81,12 @@ import {
|
||||
} 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 { retrieveAllAccountsMetadata, retrieveFullyDecryptedAccount, saveNewIdentity } from "../libs/util";
|
||||
import { logger } from "../utils/logger";
|
||||
import { Account } from "../db/tables/accounts";
|
||||
import { Account, AccountEncrypted } from "../db/tables/accounts";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
import { USE_DEXIE_DB } from "@/constants/app";
|
||||
|
||||
@Component({
|
||||
components: {},
|
||||
})
|
||||
@@ -94,23 +95,22 @@ export default class ImportAccountView extends Vue {
|
||||
$router!: Router;
|
||||
|
||||
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
|
||||
didArrays: Array<Array<string>> = [];
|
||||
didArrays: Record<string, Account[]> = {};
|
||||
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];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,60 +124,30 @@ export default class ImportAccountView extends Vue {
|
||||
|
||||
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 selectedArray: Array<Account> =
|
||||
Object.values(this.didArrays).find((dids) => dids[0].did === 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;
|
||||
}
|
||||
// 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");
|
||||
});
|
||||
// increment the last number in that max derivation path
|
||||
const newDerivPath = nextDerivationPath(
|
||||
accountWithMaxDeriv.derivationPath as string,
|
||||
);
|
||||
// we're sure there's at least one
|
||||
const maxDerivPath: string = derivationPaths[derivationPaths.length - 1] as string;
|
||||
|
||||
const mne: string = accountWithMaxDeriv.mnemonic 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 {
|
||||
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);
|
||||
await saveNewIdentity(newId, mne, newDerivPath);
|
||||
if (USE_DEXIE_DB) {
|
||||
const accountsDB = await accountsDBPromise;
|
||||
await accountsDB.accounts.add({
|
||||
@@ -191,6 +161,7 @@ export default class ImportAccountView extends Vue {
|
||||
}
|
||||
|
||||
// record that as the active DID
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
await platformService.dbExec("UPDATE settings SET activeDid = ?", [
|
||||
newId.did,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user