Skip to content

Commit

Permalink
feat(sdk-core): enable dkls cold and custody wallet creation
Browse files Browse the repository at this point in the history
Ticket: COIN-2019
  • Loading branch information
dpkjnr committed Dec 1, 2024
1 parent db8efb5 commit 6486506
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 44 deletions.
175 changes: 137 additions & 38 deletions modules/bitgo/test/v2/unit/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getSharedSecret,
BulkWalletShareOptions,
KeychainWithEncryptedPrv,
WalletWithKeychains,
} from '@bitgo/sdk-core';
import { BitGo } from '../../../src';
import { afterEach } from 'mocha';
Expand Down Expand Up @@ -849,7 +850,7 @@ describe('V2 Wallets:', function () {
});
});

describe('Generate TSS MPCv2 wallet:', async function () {
describe.only('Generate TSS MPCv2 wallet:', async function () {
const sandbox = sinon.createSandbox();

beforeEach(function () {
Expand All @@ -858,26 +859,36 @@ describe('V2 Wallets:', function () {
eth: {
walletCreationSettings: {
multiSigTypeVersion: 'MPCv2',
coldMultiSigTypeVersion: 'MPCv2',
custodialMultiSigTypeVersion: 'MPCv2',
},
},
bsc: {
walletCreationSettings: {
multiSigTypeVersion: 'MPCv2',
coldMultiSigTypeVersion: 'MPCv2',
custodialMultiSigTypeVersion: 'MPCv2',
},
},
polygon: {
walletCreationSettings: {
multiSigTypeVersion: 'MPCv2',
coldMultiSigTypeVersion: 'MPCv2',
custodialMultiSigTypeVersion: 'MPCv2',
},
},
atom: {
walletCreationSettings: {
multiSigTypeVersion: 'MPCv2',
coldMultiSigTypeVersion: 'MPCv2',
custodialMultiSigTypeVersion: 'MPCv2',
},
},
tia: {
walletCreationSettings: {
multiSigTypeVersion: 'MPCv2',
coldMultiSigTypeVersion: 'MPCv2',
custodialMultiSigTypeVersion: 'MPCv2',
},
},
},
Expand All @@ -891,7 +902,7 @@ describe('V2 Wallets:', function () {
});

['hteth', 'tbsc', 'tpolygon', 'ttia', 'tatom'].forEach((coin) => {
it(`should create a new ${coin} TSS MPCv2 wallet`, async function () {
it(`should create a new ${coin} TSS MPCv2 hot wallet`, async function () {
const testCoin = bitgo.coin(coin);
const stubbedKeychainsTriplet: KeychainsTriplet = {
userKeychain: {
Expand Down Expand Up @@ -942,6 +953,130 @@ describe('V2 Wallets:', function () {
params.passphrase
);
});

it(`should create a new ${coin} TSS MPCv2 cold wallet`, async function () {
const testCoin = bitgo.coin(coin);
const bitgoKeyId = 'key123';
const commonKeychain = '0xabc';

const bitgoKeyNock = nock('https://bitgo.fakeurl')
.get(`/api/v2/${coin}/key/${bitgoKeyId}`)
.times(1)
.reply(200, {
id: 'key123',
pub: 'bitgoPub',
type: 'tss',
source: 'bitgo',
commonKeychain,
});

const userKeyNock = nock('https://bitgo.fakeurl')
.post(`/api/v2/${coin}/key`, {
source: 'user',
keyType: 'tss',
commonKeychain,
derivedFromParentWithSeed: '37',
})
.times(1)
.reply(200, {
id: 'userKey123',
pub: 'userPub',
type: 'tss',
source: 'user',
});

const backupKeyNock = nock('https://bitgo.fakeurl')
.post(`/api/v2/${coin}/key`, {
source: 'backup',
keyType: 'tss',
commonKeychain,
derivedFromParentWithSeed: '37',
})
.times(1)
.reply(200, {
id: 'backupKey123',
pub: 'backupPub',
type: 'tss',
source: 'backup',
});

const walletNock = nock('https://bitgo.fakeurl')
.post(`/api/v2/${coin}/wallet`, {
label: 'tss wallet',
m: 2,
n: 3,
keys: ['userKey123', 'backupKey123', 'key123'],
type: 'cold',
multisigType: 'tss',
enterprise: 'enterprise',
walletVersion: 5,
})
.reply(200);

const wallets = new Wallets(bitgo, testCoin);

const params: GenerateWalletOptions = {
label: 'tss wallet',
multisigType: 'tss' as const,
enterprise: 'enterprise',
passcodeEncryptionCode: 'originalPasscodeEncryptionCode',
walletVersion: 5,
type: 'cold',
bitgoKeyId: 'key123',
commonKeychain: '0xabc',
coldDerivationSeed: '37',
};

const response = (await wallets.generateWallet(params)) as WalletWithKeychains;

bitgoKeyNock.isDone().should.be.true();
userKeyNock.isDone().should.be.true();
backupKeyNock.isDone().should.be.true();
walletNock.isDone().should.be.true();

should.exist(response.wallet);
should.exist(response.userKeychain);
should.exist(response.backupKeychain);
should.exist(response.bitgoKeychain);
response.responseType.should.equal('WalletWithKeychains');
response.userKeychain.id.should.equal('userKey123');
response.backupKeychain.id.should.equal('backupKey123');
response.bitgoKeychain.id.should.equal('key123');
});

it(`should create a new ${coin} TSS MPCv2 custody wallet`, async function () {
const testCoin = bitgo.coin(coin);
const keys = ['userKey', 'backupKey', 'bitgoKey'];

const params: GenerateWalletOptions = {
label: 'tss wallet',
passphrase: 'tss password',
multisigType: 'tss' as const,
enterprise: 'enterprise',
passcodeEncryptionCode: 'originalPasscodeEncryptionCode',
walletVersion: 5,
type: 'custodial',
};

const walletNock = nock('https://bitgo.fakeurl')
.post(`/api/v2/${coin}/wallet`)
.times(1)
.reply(200, { ...params, keys });

const wallets = new Wallets(bitgo, testCoin);

const response = (await wallets.generateWallet(params)) as WalletWithKeychains;

walletNock.isDone().should.be.true();
should.exist(response.wallet);
should.exist(response.userKeychain);
should.exist(response.backupKeychain);
should.exist(response.bitgoKeychain);
response.responseType.should.equal('WalletWithKeychains');
response.userKeychain.id.should.equal(keys[0]);
response.backupKeychain.id.should.equal(keys[1]);
response.bitgoKeychain.id.should.equal(keys[2]);
});
});

it(`should create a new hteth TSS MPCv2 wallet with walletVersion 6`, async function () {
Expand Down Expand Up @@ -1057,42 +1192,6 @@ describe('V2 Wallets:', function () {
params.passphrase
);
});

it('should throw for a cold wallet using wallet version 5', async function () {
const hteth = bitgo.coin('hteth');
const wallets = new Wallets(bitgo, hteth);

await assert.rejects(
async () => {
await wallets.generateWallet({
label: 'tss wallet',
multisigType: 'tss',
enterprise: 'enterprise',
walletVersion: 5,
type: 'cold',
});
},
{ message: 'EVM TSS MPCv2 wallets are not supported for cold wallets' }
);
});

it('should throw for a custodial wallet using wallet version 5', async function () {
const hteth = bitgo.coin('hteth');
const wallets = new Wallets(bitgo, hteth);

await assert.rejects(
async () => {
await wallets.generateWallet({
label: 'tss wallet',
multisigType: 'tss',
enterprise: 'enterprise',
walletVersion: 5,
type: 'custodial',
});
},
{ message: 'EVM TSS MPCv2 wallets are not supported for custodial wallets' }
);
});
});

describe('Generate BLS-DKG wallet:', function () {
Expand Down
24 changes: 18 additions & 6 deletions modules/sdk-core/src/bitgo/wallet/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,6 @@ export class Wallets implements IWallets {
assert(enterprise, 'enterprise is required for TSS wallet');

if (type === 'cold') {
if (params.walletVersion === 5 || params.walletVersion === 6) {
throw new Error('EVM TSS MPCv2 wallets are not supported for cold wallets');
}
// validate
assert(params.bitgoKeyId, 'bitgoKeyId is required for SMC TSS wallet');
assert(params.commonKeychain, 'commonKeychain is required for SMC TSS wallet');
Expand All @@ -331,9 +328,6 @@ export class Wallets implements IWallets {
}

if (type === 'custodial') {
if (params.walletVersion === 5 || params.walletVersion === 6) {
throw new Error('EVM TSS MPCv2 wallets are not supported for custodial wallets');
}
return this.generateCustodialMpcWallet({
multisigType: 'tss',
label,
Expand Down Expand Up @@ -1044,6 +1038,15 @@ export class Wallets implements IWallets {
const reqId = new RequestTracer();
this.bitgo.setRequestTracer(reqId);

if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') {
const tssSettings: TssSettings = await this.bitgo
.get(this.bitgo.microservicesUrl('/api/v2/tss/settings'))
.result();
const multisigTypeVersion =
tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.coldMultiSigTypeVersion;
walletVersion = this.determineEcdsaMpcWalletVersion(walletVersion, multisigTypeVersion);
}

// Create MPC Keychains
const bitgoKeychain = await this.baseCoin.keychains().get({ id: bitgoKeyId });

Expand Down Expand Up @@ -1121,6 +1124,15 @@ export class Wallets implements IWallets {
const reqId = new RequestTracer();
this.bitgo.setRequestTracer(reqId);

if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') {
const tssSettings: TssSettings = await this.bitgo
.get(this.bitgo.microservicesUrl('/api/v2/tss/settings'))
.result();
const multisigTypeVersion =
tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.custodialMultiSigTypeVersion;
walletVersion = this.determineEcdsaMpcWalletVersion(walletVersion, multisigTypeVersion);
}

const finalWalletParams = {
label,
multisigType,
Expand Down

0 comments on commit 6486506

Please sign in to comment.