From cd039f0288f7c053e78935e9d4c54d14125bf59c Mon Sep 17 00:00:00 2001 From: Samuel Holmes Date: Wed, 19 Jan 2022 16:24:39 -0800 Subject: [PATCH 1/2] Derive `primaryFormat` with a dedicated `getPrimaryFormat` utility This implements a single algorithm responsible for getting a wallet's primaryFormat. This function uses the existing algorithm for publicKeys and is now also used in `createPrivateKey`. This change should be an idempotent, but guarantees our future changes (about how we select the primary key) is couple to the appropriate routines. The goal is to future-proof our existing implementation for when we enable new wallet formats. We do not introduce any side-effects to existing wallets (wallets already persisted prior to any change). --- src/common/plugin/makeCurrencyTools.ts | 4 +- src/common/utxobased/keymanager/cleaners.ts | 41 ++++++++++++++++----- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/common/plugin/makeCurrencyTools.ts b/src/common/plugin/makeCurrencyTools.ts index e2145b57..ac46b900 100644 --- a/src/common/plugin/makeCurrencyTools.ts +++ b/src/common/plugin/makeCurrencyTools.ts @@ -16,6 +16,7 @@ import { parsePathname } from '../utxobased/engine/utils' import { asNumbWalletInfo, asPrivateKey, + getPrimaryFormat, PrivateKey } from '../utxobased/keymanager/cleaners' import { EncodeUriMetadata, ExtendedParseUri, PluginInfo } from './types' @@ -42,10 +43,11 @@ export function makeCurrencyTools( opts?: JsonObject ): Promise { const mnemonic = bip39.entropyToMnemonic(Buffer.from(io.random(32))) + const currencyFormats = engineInfo.formats ?? (['bip44'] as ['bip44']) const privateKey: PrivateKey = { seed: mnemonic, - format: opts?.format ?? engineInfo.formats?.[0] ?? 'bip44', + format: getPrimaryFormat(currencyFormats, currencyFormats), coinType: opts?.coinType ?? coinInfo.coinType ?? 0 } diff --git a/src/common/utxobased/keymanager/cleaners.ts b/src/common/utxobased/keymanager/cleaners.ts index 02bbb7cc..d0c24d9c 100644 --- a/src/common/utxobased/keymanager/cleaners.ts +++ b/src/common/utxobased/keymanager/cleaners.ts @@ -99,6 +99,31 @@ export const getSupportedFormats = ( } } +/** + * An algorithm that defines the primary format for a wallet given all of the + * wallet's formats and the currency formats (from plugin info). + * + * The purpose for this algorithm is to deterministically return a single + * primary format regardless of the order of the wallet formats given. + * The algorithm effectively takes the first format in the formats from plugin + * info that matches one of the formats in the given wallet formats. + * + * If no wallet format matches any of the formats in the plugin info, then + * the primary format is the first format in the wallet format after sorting + * in ascending order lexicographically. + */ +export const getPrimaryFormat = ( + currencyFormats: CurrencyFormat[], + walletFormats: CurrencyFormat[] +): CurrencyFormat => { + return ( + (currencyFormats.length > 0 + ? currencyFormats.find(format => walletFormats.includes(format)) + : undefined) ?? + walletFormats.sort((a, b) => (a === b ? 0 : a > b ? 1 : -1))[0] + ) +} + /** * A cleaner that desensitizes the walletInfo object, excluding sensitive * keys (seed/mnemonic, sync key, data key, etc). By using this object type @@ -147,16 +172,10 @@ export const asNumbWalletInfo = ( if (walletFormats.length === 0) { throw new Error('Missing wallet public keys') } - - // Search the engineInfo's formats array for the first format that exists - // in the publicKey data. - // If there are no defined formats in the engineInfo, then fallback to the - // first format in the publicKey after sorting alphabetically. - const primaryFormat = - (engineInfo.formats != null && engineInfo.formats.length > 0 - ? engineInfo.formats.find(format => walletFormats.includes(format)) - : undefined) ?? - walletFormats.sort((a, b) => (a === b ? 0 : a > b ? 1 : -1))[0] + const primaryFormat = getPrimaryFormat( + engineInfo.formats ?? [], + walletFormats + ) return { id, @@ -181,6 +200,8 @@ export const asNumbWalletInfo = ( id, type, keys: { + // Private key format is the primary format because it was determined + // during `createPrivateKey` phase. primaryFormat: privateKey.format, walletFormats, publicKey: { publicKeys: publicKey } From a2e5f2f11da1f10ad698374f8cf9b846895b92d4 Mon Sep 17 00:00:00 2001 From: Samuel Holmes Date: Wed, 19 Jan 2022 14:02:17 -0800 Subject: [PATCH 2/2] Make `getDisplayPublicSeed` output more correct --- src/common/utxobased/engine/makeUtxoEngine.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/utxobased/engine/makeUtxoEngine.ts b/src/common/utxobased/engine/makeUtxoEngine.ts index 48b9d125..7e80e6bf 100644 --- a/src/common/utxobased/engine/makeUtxoEngine.ts +++ b/src/common/utxobased/engine/makeUtxoEngine.ts @@ -191,8 +191,8 @@ export async function makeUtxoEngine( }, getDisplayPublicSeed(): string | null { - const xpubs = publicKey.publicKeys - return Object.values(xpubs).join('\n') + const xpubs = Object.values(publicKey.publicKeys) + return xpubs.length > 0 ? xpubs.join('\n') : null }, async getEnabledTokens(): Promise {