Skip to content

Commit e87dc8b

Browse files
committed
feat(sdk-coin-canton): added key pair generation & test
Ticket: COIN-5838
1 parent 3db7c22 commit e87dc8b

File tree

6 files changed

+487
-16
lines changed

6 files changed

+487
-16
lines changed

modules/sdk-coin-canton/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"dependencies": {
4343
"@bitgo/sdk-core": "^36.10.1",
4444
"@bitgo/statics": "^58.2.0",
45+
"@canton-network/wallet-sdk": "^0.9.0",
4546
"bignumber.js": "^9.1.1"
4647
},
4748
"devDependencies": {

modules/sdk-coin-canton/src/canton.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
} from '@bitgo/sdk-core';
1616
import { auditEddsaPrivateKey } from '@bitgo/sdk-lib-mpc';
1717
import { BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
18+
import { KeyPair as CantonKeyPair } from './lib/keyPair';
19+
import utils from './lib/utils';
1820

1921
export class Canton extends BaseCoin {
2022
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
@@ -84,12 +86,20 @@ export class Canton extends BaseCoin {
8486

8587
/** @inheritDoc */
8688
generateKeyPair(seed?: Buffer): KeyPair {
87-
throw new Error('Method not implemented.');
89+
const keyPair = seed ? new CantonKeyPair({ seed }) : new CantonKeyPair();
90+
const keys = keyPair.getKeys();
91+
if (!keys.prv) {
92+
throw new Error('Missing prv in key generation.');
93+
}
94+
return {
95+
pub: keys.pub,
96+
prv: keys.prv,
97+
};
8898
}
8999

90100
/** @inheritDoc */
91101
isValidPub(pub: string): boolean {
92-
throw new Error('Method not implemented.');
102+
return utils.isValidPublicKey(pub);
93103
}
94104

95105
/** @inheritDoc */
Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,41 @@
1-
import { DefaultKeys, Ed25519KeyPair } from '@bitgo/sdk-core';
1+
import { DefaultKeys, Ed25519KeyPair, KeyPairOptions } from '@bitgo/sdk-core';
2+
import utils from './utils';
23

34
export class KeyPair extends Ed25519KeyPair {
5+
/**
6+
* Public constructor. By default, creates a key pair with a random master seed.
7+
*
8+
* @param { KeyPairOptions } source Either a master seed, a private key, or a public key
9+
*/
10+
constructor(source?: KeyPairOptions) {
11+
super(source);
12+
}
13+
414
/** @inheritdoc */
515
getKeys(): DefaultKeys {
6-
throw new Error('Method not implemented.');
16+
const result: DefaultKeys = { pub: this.keyPair.pub };
17+
if (this.keyPair.prv) {
18+
result.prv = this.keyPair.prv;
19+
}
20+
return result;
721
}
822

923
/** @inheritdoc */
1024
recordKeysFromPrivateKeyInProtocolFormat(prv: string): DefaultKeys {
25+
// We don't use private keys for CANTON since it's implemented for TSS.
1126
throw new Error('Method not implemented.');
1227
}
1328

1429
/** @inheritdoc */
1530
recordKeysFromPublicKeyInProtocolFormat(pub: string): DefaultKeys {
16-
throw new Error('Method not implemented.');
31+
if (!utils.isValidPublicKey(pub)) {
32+
throw new Error(`Invalid public key ${pub}`);
33+
}
34+
return { pub };
1735
}
1836

1937
/** @inheritdoc */
2038
getAddress(): string {
21-
throw new Error('Method not implemented.');
39+
return utils.getAddressFromPublicKey(this.keyPair.pub);
2240
}
2341
}

modules/sdk-coin-canton/src/lib/utils.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { BaseUtils } from '@bitgo/sdk-core';
1+
import { BaseUtils, isValidEd25519PublicKey } from '@bitgo/sdk-core';
2+
import { TopologyController } from '@canton-network/wallet-sdk';
23

34
export class Utils implements BaseUtils {
45
/** @inheritdoc */
@@ -18,7 +19,7 @@ export class Utils implements BaseUtils {
1819

1920
/** @inheritdoc */
2021
isValidPublicKey(key: string): boolean {
21-
throw new Error('Method not implemented.');
22+
return isValidEd25519PublicKey(key);
2223
}
2324

2425
/** @inheritdoc */
@@ -30,6 +31,15 @@ export class Utils implements BaseUtils {
3031
isValidTransactionId(txId: string): boolean {
3132
throw new Error('Method not implemented.');
3233
}
34+
35+
/**
36+
* Method to create fingerprint (part of the canton partyId) from public key
37+
* @param {String} publicKey the public key
38+
* @returns {String}
39+
*/
40+
getAddressFromPublicKey(publicKey: string): string {
41+
return TopologyController.createFingerprintFromPublicKey(publicKey);
42+
}
3343
}
3444

3545
const utils = new Utils();
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { BitGoAPI } from '@bitgo/sdk-api';
2+
import { Eddsa } from '@bitgo/sdk-core';
3+
import { Ed25519Bip32HdTree, HDTree } from '@bitgo/sdk-lib-mpc';
4+
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
5+
import assert from 'assert';
6+
import should from 'should';
7+
8+
import { KeyPair, Tcanton } from '../../src';
9+
import utils from '../../src/lib/utils';
10+
11+
describe('Canton KeyPair', function () {
12+
let rootKeychain: string;
13+
let rootPublicKey: string;
14+
let MPC: Eddsa;
15+
let hdTree: HDTree;
16+
let bitgo: TestBitGoAPI;
17+
let basecoin: Tcanton;
18+
19+
before(async () => {
20+
hdTree = await Ed25519Bip32HdTree.initialize();
21+
MPC = await Eddsa.initialize(hdTree);
22+
const A = MPC.keyShare(1, 2, 3);
23+
const B = MPC.keyShare(2, 2, 3);
24+
const C = MPC.keyShare(3, 2, 3);
25+
26+
const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]);
27+
28+
const commonKeychain = A_combine.pShare.y + A_combine.pShare.chaincode;
29+
rootKeychain = MPC.deriveUnhardened(commonKeychain, 'm/0');
30+
rootPublicKey = Buffer.from(rootKeychain.slice(0, 64), 'hex').toString('base64');
31+
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' });
32+
bitgo.safeRegister('tcanton', Tcanton.createInstance);
33+
basecoin = bitgo.coin('tcanton') as Tcanton;
34+
});
35+
36+
describe('should create a valid KeyPair', () => {
37+
it('from an empty value', async () => {
38+
const keyPair = new KeyPair();
39+
should.exists(keyPair.getKeys().prv);
40+
should.exists(keyPair.getKeys().pub);
41+
const address = utils.getAddressFromPublicKey(keyPair.getKeys().pub);
42+
should.exists(address);
43+
});
44+
});
45+
46+
describe('Keypair from derived Public Key', () => {
47+
it('should create keypair with just derived public key', () => {
48+
const keyPair = new KeyPair({ pub: rootPublicKey });
49+
keyPair.getKeys().pub.should.equal(rootPublicKey);
50+
});
51+
52+
it('should derived ed25519 public key should be valid', () => {
53+
utils.isValidPublicKey(rootPublicKey).should.be.true();
54+
});
55+
});
56+
57+
describe('Keypair from random seed', () => {
58+
it('should generate a keypair from random seed', function () {
59+
const keyPair = basecoin.generateKeyPair();
60+
keyPair.should.have.property('pub');
61+
keyPair.should.have.property('prv');
62+
if (keyPair.pub) {
63+
basecoin.isValidPub(keyPair.pub).should.equal(true);
64+
}
65+
});
66+
});
67+
68+
describe('should fail to create a KeyPair', function () {
69+
it('from an invalid public key', () => {
70+
const source = {
71+
pub: '01D63D',
72+
};
73+
74+
assert.throws(() => new KeyPair(source));
75+
});
76+
});
77+
});

0 commit comments

Comments
 (0)