Skip to content

Commit

Permalink
chore: refactor accept share logic
Browse files Browse the repository at this point in the history
Ticket: PX-3296
  • Loading branch information
ravneet-bitgo committed Apr 18, 2024
1 parent 9e9ca5a commit a76bd1d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 42 deletions.
4 changes: 2 additions & 2 deletions modules/sdk-api/src/bitgoAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
EcdhDerivedKeypair,
EncryptOptions,
EnvironmentName,
generateRandomPassword,
getAddressP2PKH,
getSharedSecret,
GetSharingKeyOptions,
Expand Down Expand Up @@ -1205,8 +1206,7 @@ export class BitGoAPI implements BitGoBase {
* @returns {String} base58 random password
*/
generateRandomPassword(numWords = 5): string {
const bytes = sjcl.codec.bytes.fromBits(sjcl.random.randomWords(numWords));
return bs58.encode(bytes);
return generateRandomPassword(numWords);
}

/**
Expand Down
1 change: 1 addition & 0 deletions modules/sdk-core/src/bitgo/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export * from './tss';
export * from './util';
export * from './decode';
export * from './notEmpty';
export * from './wallet';

export { openpgpUtils };
12 changes: 12 additions & 0 deletions modules/sdk-core/src/bitgo/utils/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as sjcl from '@bitgo/sjcl';
import * as bs58 from 'bs58';

/**
* Generate a random password
* @param {Number} numWords Number of 32-bit words
* @returns {String} base58 random password
*/
export function generateRandomPassword(numWords: number): string {
const bytes = sjcl.codec.bytes.fromBits(sjcl.random.randomWords(numWords));
return bs58.encode(bytes);
}
7 changes: 6 additions & 1 deletion modules/sdk-core/src/bitgo/wallet/iWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,11 @@ export interface CrossChainUTXO {
export type WalletType = 'backing' | 'cold' | 'custodial' | 'custodialPaired' | 'hot' | 'trading';
export type SubWalletType = 'distributedCustody';

export type WalletUser = {
user: string;
permissions: string[];
};

export interface WalletData {
id: string;
approvalsRequired: number;
Expand Down Expand Up @@ -609,7 +614,7 @@ export interface WalletData {
tokens?: Record<string, any>[];
nfts?: { [contractAddressOrToken: string]: NftBalance };
unsupportedNfts?: { [contractAddress: string]: NftBalance };
users?: any[];
users?: WalletUser[];
}

export interface RecoverTokenOptions {
Expand Down
65 changes: 26 additions & 39 deletions modules/sdk-core/src/bitgo/wallet/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { IBaseCoin, KeychainsTriplet, SupplementGenerateWalletOptions } from '..
import { BitGoBase } from '../bitgoBase';
import { getSharedSecret } from '../ecdh';
import { AddKeychainOptions, Keychain } from '../keychain';
import { promiseProps, RequestTracer } from '../utils';
import { generateRandomPassword, promiseProps, RequestTracer } from '../utils';
import {
AcceptShareOptions,
AddWalletOptions,
Expand All @@ -27,8 +27,8 @@ import {
WalletWithKeychains,
} from './iWallets';
import { Wallet } from './wallet';
import * as sjcl from '@bitgo/sjcl';
import * as bs58 from 'bs58';

const debug = require('debug')('bitgo:v2:wallets');

export class Wallets implements IWallets {
private readonly bitgo: BitGoBase;
Expand Down Expand Up @@ -547,56 +547,44 @@ export class Wallets implements IWallets {
*/
async reshareOfcAccountWithSpenders(walletId: string, userPassword: string): Promise<void> {
const wallet = await this.bitgo.coin('ofc').wallets().get({ id: walletId });
const enterpriseUsersResponse = (await this.bitgo
if (!wallet?._wallet?.enterprise) {
throw new Error('Enterprise not found for the wallet');
}

const enterpriseUsersResponse = await this.bitgo
.get(`/api/v2/enterprise/${wallet?._wallet?.enterprise}/user`)
.result()) as any;
.result();
// create a map of users for easy lookup - we need the user email id to share the wallet
const usersMap = new Map(
[...enterpriseUsersResponse?.adminUsers, ...enterpriseUsersResponse?.nonAdminUsers].map((obj) => [obj.id, obj])
);
wallet?._wallet?.users?.forEach(async (user) => {
try {
// user should be a spender and not an admin
if (user.permissions.includes('spend') && !user.permissions.includes('admin')) {
const userObject = usersMap.get(user.user);
const shareParams = {
walletId: walletId,
user: user.user,
permissions: user.permissions.join(','),
walletPassphrase: userPassword,
email: userObject.email.email,
coin: wallet.coin,
reshare: true,
};
wallet.shareWallet(shareParams);
}
} catch (e) {
// TODO: gracefully handle this error
console.error(e);
// user should be a spender and not an admin
if (user.permissions.includes('spend') && !user.permissions.includes('admin')) {
const userObject = usersMap.get(user.user);
const shareParams = {
walletId: walletId,
user: user.user,
permissions: user.permissions.join(','),
walletPassphrase: userPassword,
email: userObject.email.email,
coin: wallet.coin,
reshare: true,
};
wallet.shareWallet(shareParams);
}
});
}

/**
* Generate a random password
* @param {Number} numWords Number of 32-bit words
* @returns {String} base58 random password
*/
generateRandomPassword(numWords = 5): string {
const bytes = sjcl.codec.bytes.fromBits(sjcl.random.randomWords(numWords));
return bs58.encode(bytes);
}

/**
* Create keychain for ofc wallet using the password
* @param userPassword
* @returns
*/
async createKeychain(userPassword: string): Promise<Keychain> {
const sdkCoin = await this.bitgo.coin('ofc');
const keychains = sdkCoin.keychains();
const keychains = this.baseCoin.keychains();
const newKeychain = keychains.create();
const originalPasscodeEncryptionCode = this.generateRandomPassword();
const originalPasscodeEncryptionCode = generateRandomPassword(5);

const encryptedPrv = this.bitgo.encrypt({
password: userPassword,
Expand Down Expand Up @@ -627,7 +615,7 @@ export class Wallets implements IWallets {
common.validateParams(params, ['walletShareId'], ['overrideEncryptedPrv', 'userPassword', 'newWalletPassphrase']);

let encryptedPrv = params.overrideEncryptedPrv;
const walletShare = (await this.getShare({ walletShareId: params.walletShareId })) as any;
const walletShare = await this.getShare({ walletShareId: params.walletShareId });
if (walletShare.keychainOverrideRequired && walletShare.permissions.indexOf('admin') !== -1) {
if (_.isUndefined(params.userPassword)) {
throw new Error('userPassword param must be provided to decrypt shared key');
Expand All @@ -643,8 +631,7 @@ export class Wallets implements IWallets {
try {
await this.reshareOfcAccountWithSpenders(walletShare.wallet, params.userPassword);
} catch (e) {
// TODO: gracefully handle this error
console.error(e);
debug('failed to reshare wallet with spenders', e);
}
}
return response;
Expand Down

0 comments on commit a76bd1d

Please sign in to comment.