From b284738269335288ab5a38a609ec8cc7bb6413a4 Mon Sep 17 00:00:00 2001 From: Ravneet Sandhu Date: Tue, 16 Apr 2024 13:59:27 +0530 Subject: [PATCH] chore: refactor accept share logic Ticket: PX-3296 --- modules/sdk-api/src/bitgoAPI.ts | 4 +- modules/sdk-core/src/bitgo/utils/index.ts | 1 + modules/sdk-core/src/bitgo/utils/ofc.ts | 12 ++++ modules/sdk-core/src/bitgo/wallet/iWallet.ts | 7 ++- modules/sdk-core/src/bitgo/wallet/wallets.ts | 63 ++++++++------------ 5 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 modules/sdk-core/src/bitgo/utils/ofc.ts diff --git a/modules/sdk-api/src/bitgoAPI.ts b/modules/sdk-api/src/bitgoAPI.ts index 79f0f65a8f..eb9aedc881 100644 --- a/modules/sdk-api/src/bitgoAPI.ts +++ b/modules/sdk-api/src/bitgoAPI.ts @@ -11,6 +11,7 @@ import { EcdhDerivedKeypair, EncryptOptions, EnvironmentName, + generateRandomPassword, getAddressP2PKH, getSharedSecret, GetSharingKeyOptions, @@ -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); } /** diff --git a/modules/sdk-core/src/bitgo/utils/index.ts b/modules/sdk-core/src/bitgo/utils/index.ts index e12fb9b0eb..49693db2eb 100644 --- a/modules/sdk-core/src/bitgo/utils/index.ts +++ b/modules/sdk-core/src/bitgo/utils/index.ts @@ -11,5 +11,6 @@ export * from './tss'; export * from './util'; export * from './decode'; export * from './notEmpty'; +export * from './ofc'; export { openpgpUtils }; diff --git a/modules/sdk-core/src/bitgo/utils/ofc.ts b/modules/sdk-core/src/bitgo/utils/ofc.ts new file mode 100644 index 0000000000..ad16bd7f49 --- /dev/null +++ b/modules/sdk-core/src/bitgo/utils/ofc.ts @@ -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); +} diff --git a/modules/sdk-core/src/bitgo/wallet/iWallet.ts b/modules/sdk-core/src/bitgo/wallet/iWallet.ts index 58f16ae6dd..b1abb342f7 100644 --- a/modules/sdk-core/src/bitgo/wallet/iWallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/iWallet.ts @@ -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; @@ -609,7 +614,7 @@ export interface WalletData { tokens?: Record[]; nfts?: { [contractAddressOrToken: string]: NftBalance }; unsupportedNfts?: { [contractAddress: string]: NftBalance }; - users?: any[]; + users?: WalletUser[]; } export interface RecoverTokenOptions { diff --git a/modules/sdk-core/src/bitgo/wallet/wallets.ts b/modules/sdk-core/src/bitgo/wallet/wallets.ts index 904217d407..cfc5a2f6a3 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallets.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallets.ts @@ -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, @@ -30,6 +30,8 @@ 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; private readonly baseCoin: IBaseCoin; @@ -547,56 +549,44 @@ export class Wallets implements IWallets { */ async reshareOfcAccountWithSpenders(walletId: string, userPassword: string): Promise { 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 { - 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, @@ -627,7 +617,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'); @@ -643,8 +633,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;