Skip to content

Commit 48d0f5a

Browse files
authored
Merge pull request #7526 from BitGo/WP-6780-walletkeyshare-multi-key
feat(sdk-core): exclude keychains for multi-user-key
2 parents b6331ca + 2f95b23 commit 48d0f5a

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

modules/bitgo/test/v2/unit/wallet.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,148 @@ describe('V2 Wallet:', function () {
20692069
getSharingKeyNock.isDone().should.be.True();
20702070
getKeyNock.isDone().should.be.True();
20712071
});
2072+
2073+
describe('OFC Multi-User-Key Wallet Sharing', function () {
2074+
const userId = '123';
2075+
const permissions = 'view,spend';
2076+
let ofcWallet: Wallet;
2077+
let ofcMultiUserKeyWallet: Wallet;
2078+
2079+
before(function () {
2080+
const ofcCoin = bitgo.coin('ofc');
2081+
2082+
// Regular OFC wallet without multi-user-key feature
2083+
const regularOfcWalletData = {
2084+
id: '5b34252f1bf349930e3400c00000000',
2085+
coin: 'ofc',
2086+
keys: [
2087+
'5b3424f91bf349930e34018100000000',
2088+
'5b3424f91bf349930e34018200000000',
2089+
'5b3424f91bf349930e34018300000000',
2090+
],
2091+
coinSpecific: {},
2092+
multisigType: 'onchain',
2093+
type: 'hot',
2094+
} as any;
2095+
ofcWallet = new Wallet(bitgo, ofcCoin, regularOfcWalletData);
2096+
2097+
// OFC wallet with multi-user-key feature
2098+
const multiUserKeyWalletData = {
2099+
id: '5b34252f1bf349930e3400d00000000',
2100+
coin: 'ofc',
2101+
keys: [
2102+
'5b3424f91bf349930e34018400000000',
2103+
'5b3424f91bf349930e34018500000000',
2104+
'5b3424f91bf349930e34018600000000',
2105+
],
2106+
coinSpecific: {
2107+
features: ['multi-user-key'],
2108+
},
2109+
multisigType: 'onchain',
2110+
type: 'hot',
2111+
} as any;
2112+
ofcMultiUserKeyWallet = new Wallet(bitgo, ofcCoin, multiUserKeyWalletData);
2113+
});
2114+
2115+
afterEach(function () {
2116+
sinon.restore();
2117+
nock.cleanAll();
2118+
});
2119+
2120+
it('should exclude keychain property for multi-user-key wallets in createShare', async function () {
2121+
const createShareParams = {
2122+
user: userId,
2123+
permissions,
2124+
};
2125+
2126+
const createShareNock = nock(bgUrl)
2127+
.post(`/api/v2/ofc/wallet/${ofcMultiUserKeyWallet.id()}/share`, (body) => {
2128+
// Verify that keychain is not included in the request
2129+
body.should.not.have.property('keychain');
2130+
body.user.should.equal(userId);
2131+
body.permissions.should.equal(permissions);
2132+
return true;
2133+
})
2134+
.reply(200, {});
2135+
2136+
await ofcMultiUserKeyWallet.createShare(createShareParams);
2137+
2138+
createShareNock.isDone().should.be.True();
2139+
});
2140+
2141+
it('should throw error when keychain is provided for multi-user-key wallets in createShare', async function () {
2142+
const createShareParams = {
2143+
user: userId,
2144+
permissions,
2145+
keychain: {
2146+
pub: 'xpub661MyMwAqRbcFXDcWD2vxuebcT1ZpTF4Vke6qmMW8yzddwNYpAPjvYEEL5jLfyYXW2fuxtAxY8TgjPUJLcf1C8qz9N6VgZxArKX4EwB8rH5',
2147+
encryptedPrv: 'encrypted',
2148+
fromPubKey: 'fromPub',
2149+
toPubKey: 'toPub',
2150+
path: 'm/999999/1/1',
2151+
},
2152+
};
2153+
2154+
await ofcMultiUserKeyWallet
2155+
.createShare(createShareParams)
2156+
.should.be.rejectedWith('keychain property must not be provided for multi-user-key wallets');
2157+
});
2158+
2159+
it('should include keychain property for non-multi-user-key OFC wallets', async function () {
2160+
const createShareParams = {
2161+
user: userId,
2162+
permissions,
2163+
keychain: {
2164+
pub: 'xpub661MyMwAqRbcFXDcWD2vxuebcT1ZpTF4Vke6qmMW8yzddwNYpAPjvYEEL5jLfyYXW2fuxtAxY8TgjPUJLcf1C8qz9N6VgZxArKX4EwB8rH5',
2165+
encryptedPrv: 'encrypted',
2166+
fromPubKey: 'fromPub',
2167+
toPubKey: 'toPub',
2168+
path: 'm/999999/1/1',
2169+
},
2170+
};
2171+
2172+
const createShareNock = nock(bgUrl)
2173+
.post(`/api/v2/ofc/wallet/${ofcWallet.id()}/share`, (body) => {
2174+
// Verify that keychain IS included in the request for regular wallets
2175+
body.should.have.property('keychain');
2176+
body.keychain.should.have.property('pub');
2177+
body.keychain.should.have.property('encryptedPrv');
2178+
body.keychain.should.have.property('fromPubKey');
2179+
body.keychain.should.have.property('toPubKey');
2180+
body.keychain.should.have.property('path');
2181+
body.user.should.equal(userId);
2182+
body.permissions.should.equal(permissions);
2183+
return true;
2184+
})
2185+
.reply(200, {});
2186+
2187+
await ofcWallet.createShare(createShareParams);
2188+
2189+
createShareNock.isDone().should.be.True();
2190+
});
2191+
2192+
it('should handle empty keychain object for multi-user-key wallets', async function () {
2193+
const createShareParams = {
2194+
user: userId,
2195+
permissions,
2196+
keychain: {},
2197+
};
2198+
2199+
const createShareNock = nock(bgUrl)
2200+
.post(`/api/v2/ofc/wallet/${ofcMultiUserKeyWallet.id()}/share`, (body) => {
2201+
// Verify that keychain is not included in the request even if passed as empty
2202+
body.should.not.have.property('keychain');
2203+
body.user.should.equal(userId);
2204+
body.permissions.should.equal(permissions);
2205+
return true;
2206+
})
2207+
.reply(200, {});
2208+
2209+
await ofcMultiUserKeyWallet.createShare(createShareParams);
2210+
2211+
createShareNock.isDone().should.be.True();
2212+
});
2213+
});
20722214
});
20732215

20742216
describe('Wallet Freezing', function () {

modules/sdk-core/src/bitgo/wallet/iWallet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ export interface WalletCoinSpecific {
328328
walletVersion?: number;
329329
hashAlgorithm?: string;
330330
pendingEcdsaTssInitialization?: boolean;
331+
features?: string[];
331332
/**
332333
* Lightning coin specific data starts
333334
*/

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,13 +1594,27 @@ export class Wallet implements IWallet {
15941594
return userPrv;
15951595
}
15961596

1597+
private isMultiUserKeyWallet(): boolean {
1598+
return this._wallet.coinSpecific?.features?.includes('multi-user-key') ?? false;
1599+
}
1600+
15971601
/**
15981602
* Send an encrypted wallet share to BitGo.
15991603
* @param params
16001604
*/
16011605
async createShare(params: CreateShareOptions = {}): Promise<any> {
16021606
common.validateParams(params, ['user', 'permissions'], []);
16031607

1608+
const isMultiUserKeyWallet = this.isMultiUserKeyWallet();
1609+
1610+
if (isMultiUserKeyWallet) {
1611+
if (params.keychain && !_.isEmpty(params.keychain)) {
1612+
throw new Error('keychain property must not be provided for multi-user-key wallets');
1613+
}
1614+
// Remove keychain from params if presents
1615+
return await this.bitgo.post(this.url('/share')).send(_.omit(params, 'keychain')).result();
1616+
}
1617+
16041618
if (params.keychain && !_.isEmpty(params.keychain)) {
16051619
if (
16061620
!params.keychain.pub ||

0 commit comments

Comments
 (0)