Skip to content

Commit

Permalink
feat: on restoreWalletAccounts, only support stxPrivateKey (#1204)
Browse files Browse the repository at this point in the history
* feat: on restoreWalletAccounts only support stxPrivateKey

* fix: make network parameter required

* refactor: default to mainnet

Co-authored-by: Friedger Müffke <[email protected]>
Co-authored-by: janniks <[email protected]>
  • Loading branch information
3 people authored Mar 2, 2022
1 parent e1c8c9c commit 1fbd30e
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 111 deletions.
1 change: 1 addition & 0 deletions packages/wallet-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ const restoredWallet = await restoreWalletAccounts({
// `baseWallet` is returned from `generateWallet`
wallet: baseWallet,
gaiaHubUrl: 'https://hub.blockstack.org',
network: new StacksMainnet(),
});
```

Expand Down
34 changes: 32 additions & 2 deletions packages/wallet-sdk/src/derive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createSha2Hash, ecPairToHexString } from '@stacks/encryption';

import { assertIsTruthy, whenChainId } from './utils';
import { Account, WalletKeys } from './models/common';
import { StacksNetwork } from '@stacks/network';
import { StacksMainnet, StacksNetwork } from '@stacks/network';
import { getAddressFromPrivateKey } from '@stacks/transactions';
import { fetchFirstName } from './usernames';

Expand Down Expand Up @@ -87,7 +87,8 @@ export enum DerivationType {

/**
* Tries to find a derivation path for the stxPrivateKey for the account
* defined by rootNode and index that respects the username of that account.
* defined by rootNode and index that respects the username of that account
* and that respects the given derivationType.
*
* The stxPrivateKey is used to sign the profile of the account, therefore,
* a username must be owned by the stxPrivateKey.
Expand All @@ -97,6 +98,9 @@ export enum DerivationType {
*
* If no username is provided, a lookup for names owned
* by the stx derivation path and by the data derivation path is done.
*
* If derivationType other than Unknown is given this derivation type is enforced.
*
* @param selectionOptions
* @returns username and derivation type
*/
Expand Down Expand Up @@ -197,9 +201,35 @@ const selectUsernameForAccount = async ({
}
}
// use wallet derivation for accounts without username
// or without network parameter (offline)
return { username: undefined, derivationType: DerivationType.Wallet };
};

export const fetchUsernameForAccountByDerivationType = async ({
rootNode,
index,
derivationType,
network,
}: {
rootNode: BIP32Interface;
index: number;
derivationType: DerivationType.Wallet | DerivationType.Data;
network?: StacksNetwork;
}): Promise<{
username: string | undefined;
}> => {
// try to find existing usernames owned by given derivation path
const selectedNetwork = network ?? new StacksMainnet();
const txVersion = whenChainId(selectedNetwork.chainId)({
[ChainID.Mainnet]: TransactionVersion.Mainnet,
[ChainID.Testnet]: TransactionVersion.Testnet,
});
const privateKey = derivePrivateKeyByType({ rootNode, index, derivationType });
const address = getAddressFromPrivateKey(privateKey, txVersion);
const username = await fetchFirstName(address, selectedNetwork);
return { username };
};

export const derivePrivateKeyByType = ({
rootNode,
index,
Expand Down
38 changes: 12 additions & 26 deletions packages/wallet-sdk/src/models/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StacksNetwork } from '@stacks/network';
import { DerivationType, derivePrivateKeyByType, selectStxDerivation } from '..';
import { DerivationType, deriveStxPrivateKey, fetchUsernameForAccountByDerivationType } from '..';
import { deriveAccount, deriveLegacyConfigPrivateKey } from '../derive';
import { connectToGaiaHubWithConfig, getHubInfo } from '../utils';
import { Wallet, getRootNode } from './common';
Expand All @@ -24,7 +24,7 @@ export async function restoreWalletAccounts({
}: {
wallet: Wallet;
gaiaHubUrl: string;
network?: StacksNetwork;
network: StacksNetwork;
}): Promise<Wallet> {
const hubInfo = await getHubInfo(gaiaHubUrl);
const rootNode = getRootNode(wallet);
Expand All @@ -49,34 +49,27 @@ export async function restoreWalletAccounts({
walletConfig.accounts.length >= (legacyWalletConfig?.identities.length || 0)
) {
const newAccounts = await Promise.all(
walletConfig.accounts.map(async (account, index) => {
walletConfig.accounts.map(async (_, index) => {
let existingAccount = wallet.accounts[index];
const { username, stxDerivationType } = await selectStxDerivation({
username: account.username,
const { username } = await fetchUsernameForAccountByDerivationType({
rootNode,
index,
derivationType: DerivationType.Wallet,
network,
});
if (stxDerivationType === DerivationType.Unknown) {
// This account index has a username
// that is not owned by stx derivation path or data derivation path
// we can't determine the stx private key :-/
return Promise.reject(`Username ${username} is owned by unknown private key`);
}
if (!existingAccount) {
existingAccount = deriveAccount({
rootNode,
index,
salt: wallet.salt,
stxDerivationType,
stxDerivationType: DerivationType.Wallet,
});
} else {
existingAccount = {
...existingAccount,
stxPrivateKey: derivePrivateKeyByType({
stxPrivateKey: deriveStxPrivateKey({
rootNode,
index,
derivationType: stxDerivationType,
}),
};
}
Expand All @@ -96,34 +89,27 @@ export async function restoreWalletAccounts({
// Restore from legacy config, and upload a new one
if (legacyWalletConfig) {
const newAccounts = await Promise.all(
legacyWalletConfig.identities.map(async (identity, index) => {
legacyWalletConfig.identities.map(async (_, index) => {
let existingAccount = wallet.accounts[index];
const { username, stxDerivationType } = await selectStxDerivation({
username: identity.username,
const { username } = await fetchUsernameForAccountByDerivationType({
rootNode,
index,
derivationType: DerivationType.Wallet,
network,
});
if (stxDerivationType === DerivationType.Unknown) {
// This account index has a username
// that is not owned by stx derivation path or data derivation path
// we can't determine the stx private key :-/
return Promise.reject(`Username ${username} is owned by unknown private key`);
}
if (!existingAccount) {
existingAccount = deriveAccount({
rootNode,
index,
salt: wallet.salt,
stxDerivationType,
stxDerivationType: DerivationType.Wallet,
});
} else {
existingAccount = {
...existingAccount,
stxPrivateKey: derivePrivateKeyByType({
stxPrivateKey: deriveStxPrivateKey({
rootNode,
index,
derivationType: stxDerivationType,
}),
};
}
Expand Down
Loading

1 comment on commit 1fbd30e

@vercel
Copy link

@vercel vercel bot commented on 1fbd30e Mar 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.