|
@ -20,18 +20,18 @@ |
|
|
Will increment the maximum known derivation path from the existing seed. |
|
|
Will increment the maximum known derivation path from the existing seed. |
|
|
</p> |
|
|
</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. |
|
|
Choose existing DIDs from same seed phrase to compute derivation. |
|
|
</p> |
|
|
</p> |
|
|
<ul class="mb-4"> |
|
|
<ul class="mb-4"> |
|
|
<li |
|
|
<li |
|
|
v-for="dids in didArrays" |
|
|
v-for="dids in Object.values(didArrays)" |
|
|
:key="dids[0]" |
|
|
:key="dids[0].did" |
|
|
class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2" |
|
|
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 |
|
|
<font-awesome |
|
|
v-if="dids[0] == selectedArrayFirstDid" |
|
|
v-if="dids[0].did == selectedArrayFirstDid" |
|
|
icon="circle" |
|
|
icon="circle" |
|
|
class="fa-fw text-blue-500 text-xl mr-3" |
|
|
class="fa-fw text-blue-500 text-xl mr-3" |
|
|
></font-awesome> |
|
|
></font-awesome> |
|
@ -41,8 +41,8 @@ |
|
|
class="fa-fw text-slate-400 text-xl mr-3" |
|
|
class="fa-fw text-slate-400 text-xl mr-3" |
|
|
></font-awesome> |
|
|
></font-awesome> |
|
|
<span class="overflow-hidden"> |
|
|
<span class="overflow-hidden"> |
|
|
<div class="text-sm text-slate-500 truncate"> |
|
|
<div class="text-sm text-slate-500"> |
|
|
<code>{{ dids.join(",") }}</code> |
|
|
<code>{{ dids.map((d) => d.did).join(" ") }}</code> |
|
|
</div> |
|
|
</div> |
|
|
</span> |
|
|
</span> |
|
|
</li> |
|
|
</li> |
|
@ -69,6 +69,7 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script lang="ts"> |
|
|
<script lang="ts"> |
|
|
|
|
|
import * as R from "ramda"; |
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
import { Router, RouteLocationNormalizedLoaded } from "vue-router"; |
|
|
import { Router, RouteLocationNormalizedLoaded } from "vue-router"; |
|
|
|
|
|
|
|
@ -80,12 +81,12 @@ import { |
|
|
} from "../libs/crypto"; |
|
|
} from "../libs/crypto"; |
|
|
import { accountsDBPromise, db } from "../db/index"; |
|
|
import { accountsDBPromise, db } from "../db/index"; |
|
|
import { MASTER_SETTINGS_KEY } from "../db/tables/settings"; |
|
|
import { MASTER_SETTINGS_KEY } from "../db/tables/settings"; |
|
|
import * as databaseUtil from "../db/databaseUtil"; |
|
|
import { retrieveAllAccountsMetadata, retrieveFullyDecryptedAccount, saveNewIdentity } from "../libs/util"; |
|
|
import { retrieveAllAccountsMetadata } from "../libs/util"; |
|
|
|
|
|
import { logger } from "../utils/logger"; |
|
|
import { logger } from "../utils/logger"; |
|
|
import { Account } from "../db/tables/accounts"; |
|
|
import { Account, AccountEncrypted } from "../db/tables/accounts"; |
|
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; |
|
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; |
|
|
import { USE_DEXIE_DB } from "@/constants/app"; |
|
|
import { USE_DEXIE_DB } from "@/constants/app"; |
|
|
|
|
|
|
|
|
@Component({ |
|
|
@Component({ |
|
|
components: {}, |
|
|
components: {}, |
|
|
}) |
|
|
}) |
|
@ -94,23 +95,22 @@ export default class ImportAccountView extends Vue { |
|
|
$router!: Router; |
|
|
$router!: Router; |
|
|
|
|
|
|
|
|
derivationPath = DEFAULT_ROOT_DERIVATION_PATH; |
|
|
derivationPath = DEFAULT_ROOT_DERIVATION_PATH; |
|
|
didArrays: Array<Array<string>> = []; |
|
|
didArrays: Record<string, Account[]> = {}; |
|
|
selectedArrayFirstDid = ""; |
|
|
selectedArrayFirstDid = ""; |
|
|
|
|
|
|
|
|
async mounted() { |
|
|
async mounted() { |
|
|
const accounts: Account[] = await retrieveAllAccountsMetadata(); |
|
|
const accounts: AccountEncrypted[] = await retrieveAllAccountsMetadata(); |
|
|
const seedDids: Record<string, Array<string>> = {}; |
|
|
const decryptedAccounts: (Account | undefined)[] = await Promise.all(accounts.map(async (account) => { |
|
|
accounts.forEach((account) => { |
|
|
return retrieveFullyDecryptedAccount(account.did); |
|
|
// Since we're only getting metadata, we can't check mnemonic |
|
|
})); |
|
|
// Instead, we'll group by derivation path |
|
|
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter((account) => account !== undefined); |
|
|
if (account.derivationPath) { |
|
|
|
|
|
const prevDids: Array<string> = seedDids[account.derivationPath] || []; |
|
|
// group by account.mnemonic |
|
|
seedDids[account.derivationPath] = prevDids.concat([account.did]); |
|
|
const groupedAccounts: Record<string, Account[]> = R.groupBy((a) => a.mnemonic || "", filteredDecryptedAccounts) as Record<string, Account[]>; |
|
|
} |
|
|
|
|
|
}); |
|
|
this.didArrays = groupedAccounts; |
|
|
this.didArrays = Object.values(seedDids); |
|
|
if (Object.keys(this.didArrays).length > 0) { |
|
|
if (this.didArrays.length > 0) { |
|
|
this.selectedArrayFirstDid = Object.values(this.didArrays)[0][0].did; |
|
|
this.selectedArrayFirstDid = this.didArrays[0][0]; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -124,60 +124,30 @@ export default class ImportAccountView extends Vue { |
|
|
|
|
|
|
|
|
public async incrementDerivation() { |
|
|
public async incrementDerivation() { |
|
|
// find the maximum derivation path for the selected DIDs |
|
|
// find the maximum derivation path for the selected DIDs |
|
|
const selectedArray: Array<string> = |
|
|
const selectedArray: Array<Account> = |
|
|
this.didArrays.find((dids) => dids[0] === this.selectedArrayFirstDid) || |
|
|
Object.values(this.didArrays).find((dids) => dids[0].did === this.selectedArrayFirstDid) || |
|
|
[]; |
|
|
[]; |
|
|
const platformService = PlatformServiceFactory.getInstance(); |
|
|
// extract the derivationPath array and sort it |
|
|
const qmarks = selectedArray.map(() => "?").join(","); |
|
|
const derivationPaths = selectedArray.map((account) => account.derivationPath); |
|
|
const queryResult = await platformService.dbQuery( |
|
|
derivationPaths.sort((a, b) => { |
|
|
`SELECT * FROM accounts WHERE did IN (${qmarks})`, |
|
|
const aParts = a?.split("/"); |
|
|
selectedArray, |
|
|
const aLast = aParts?.[aParts.length - 1]; |
|
|
); |
|
|
const bParts = b?.split("/"); |
|
|
let allMatchingAccounts = databaseUtil.mapQueryResultToValues( |
|
|
const bLast = bParts?.[bParts.length - 1]; |
|
|
queryResult, |
|
|
return parseInt(aLast || "0") - parseInt(bLast || "0"); |
|
|
) 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 |
|
|
// we're sure there's at least one |
|
|
const newDerivPath = nextDerivationPath( |
|
|
const maxDerivPath: string = derivationPaths[derivationPaths.length - 1] as string; |
|
|
accountWithMaxDeriv.derivationPath 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 [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath); |
|
|
|
|
|
|
|
|
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath); |
|
|
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath); |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
const { sql, params } = databaseUtil.generateInsertStatement( |
|
|
await saveNewIdentity(newId, mne, newDerivPath); |
|
|
{ |
|
|
|
|
|
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) { |
|
|
if (USE_DEXIE_DB) { |
|
|
const accountsDB = await accountsDBPromise; |
|
|
const accountsDB = await accountsDBPromise; |
|
|
await accountsDB.accounts.add({ |
|
|
await accountsDB.accounts.add({ |
|
@ -191,6 +161,7 @@ export default class ImportAccountView extends Vue { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// record that as the active DID |
|
|
// record that as the active DID |
|
|
|
|
|
const platformService = PlatformServiceFactory.getInstance(); |
|
|
await platformService.dbExec("UPDATE settings SET activeDid = ?", [ |
|
|
await platformService.dbExec("UPDATE settings SET activeDid = ?", [ |
|
|
newId.did, |
|
|
newId.did, |
|
|
]); |
|
|
]); |
|
|