Skip to content

Commit eab7e82

Browse files
committed
feat(ebe): added multisig signing API to EBE
Ticket: WP-4519
1 parent 95b9566 commit eab7e82

File tree

5 files changed

+98
-2
lines changed

5 files changed

+98
-2
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as express from 'express';
2+
import { KmsClient } from '../../kms/kmsClient';
3+
import { BitGo } from 'bitgo';
4+
5+
export async function signMultisigTransaction(
6+
req: express.Request,
7+
res: express.Response,
8+
): Promise<any> {
9+
console.log(req);
10+
const { source, pub }: { source: string; pub: string; txPrebuild: any } = req.body;
11+
12+
if (!source || !pub) {
13+
throw new Error('Source and public key are required for signing');
14+
}
15+
16+
const bitgo: BitGo = req.body.bitgo;
17+
const kms = new KmsClient();
18+
19+
// Retrieve the private key from KMS
20+
let prv: string;
21+
try {
22+
const res = await kms.getKey({ pub, source });
23+
prv = res.prv;
24+
} catch (error: any) {
25+
res.status(error.status || 500).json({
26+
message: error.message || 'Failed to retrieve key from KMS',
27+
});
28+
return;
29+
}
30+
31+
// clean up request body
32+
delete req.body.bitgo;
33+
delete req.body.source;
34+
delete req.body.pub;
35+
36+
// Sign the transaction using BitGo SDK
37+
const coin = bitgo.coin(req.params.coin);
38+
try {
39+
return await coin.signTransaction({ ...req.body, prv });
40+
} catch (error) {
41+
console.log('error while signing wallet transaction ', error);
42+
throw error;
43+
}
44+
}

src/kms/kmsClient.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import debug from 'debug';
22
import * as superagent from 'superagent';
33
import { config, isMasterExpressConfig } from '../config';
44
import { PostKeyKmsSchema, PostKeyParams, PostKeyResponse } from './types/postKey';
5+
import { GetKeyKmsSchema, GetKeyParams, GetKeyResponse } from './types/getKey';
56

67
const debugLogger = debug('bitgo:express:kmsClient');
78

@@ -25,6 +26,7 @@ export class KmsClient {
2526
async postKey(params: PostKeyParams): Promise<PostKeyResponse> {
2627
debugLogger('Posting key to KMS: %O', params);
2728

29+
// Call KMS to post the key
2830
let kmsResponse: any;
2931
try {
3032
kmsResponse = await superagent.post(`${this.url}/key`).set('x-api-key', 'abc').send(params);
@@ -33,6 +35,7 @@ export class KmsClient {
3335
throw error;
3436
}
3537

38+
// validate the response
3639
try {
3740
PostKeyKmsSchema.parse(kmsResponse.body);
3841
} catch (error: any) {
@@ -44,4 +47,31 @@ export class KmsClient {
4447
const { pub, coin, source } = kmsResponse.body;
4548
return { pub, coin, source } as PostKeyResponse;
4649
}
50+
51+
async getKey(params: GetKeyParams): Promise<GetKeyResponse> {
52+
debugLogger('Getting key from KMS: %O', params);
53+
54+
// Call KMS to get the key
55+
let kmsResponse: any;
56+
try {
57+
kmsResponse = await superagent
58+
.get(`${this.url}/key/${params.pub}`)
59+
.set('x-api-key', 'abc')
60+
.query({ source: params.source });
61+
} catch (error: any) {
62+
console.log('Error getting key from KMS', error);
63+
throw error;
64+
}
65+
66+
// validate the response
67+
try {
68+
GetKeyKmsSchema.parse(kmsResponse.body);
69+
} catch (error: any) {
70+
throw new Error(
71+
`KMS returned unexpected response${error.message ? `: ${error.message}` : ''}`,
72+
);
73+
}
74+
75+
return kmsResponse.body as GetKeyResponse;
76+
}
4777
}

src/kms/types/getKey.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as z from 'zod';
2+
3+
export interface GetKeyParams {
4+
pub: string;
5+
source: string;
6+
}
7+
8+
export interface GetKeyResponse {
9+
pub: string;
10+
prv: string;
11+
source: 'user' | 'backup';
12+
type: 'independent' | 'tss';
13+
}
14+
15+
export const GetKeyKmsSchema = z.object({
16+
pub: z.string(),
17+
prv: z.string(),
18+
source: z.enum(['user', 'backup']),
19+
type: z.enum(['independent', 'tss']),
20+
});

src/kms/types/postKey.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ export interface PostKeyParams {
55
pub: string;
66
coin: string;
77
source: string;
8-
type: 'independent' | 'enclaved';
8+
type: 'independent' | 'tss';
99
seed?: string; // Optional seed for key generation
1010
}
1111

1212
export interface PostKeyResponse {
1313
pub: string;
1414
coin: string;
1515
source: string;
16-
type: 'independent' | 'enclaved';
16+
type: 'independent' | 'tss';
1717
}
1818

1919
export const PostKeyKmsSchema = z.object({

src/routes/enclaved.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import pjson from '../../package.json';
44
import type { BitGoOptions } from 'bitgo';
55
import { postIndependentKey } from '../api/enclaved/postIndependentKey';
66
import { promiseWrapper } from './utils';
7+
import { signMultisigTransaction } from '../api/enclaved/signMultisigTransaction';
78

89
const debugLogger = debug('enclaved:routes');
910

@@ -50,6 +51,7 @@ async function prepBitGo(req: express.Request, res: express.Response, next: expr
5051

5152
function setupKeyGenRoutes(app: express.Application) {
5253
app.post('/api/:coin/key/independent', prepBitGo, promiseWrapper(postIndependentKey));
54+
app.post('/api/:coin/multisig/sign', prepBitGo, promiseWrapper(signMultisigTransaction));
5355
debugLogger('KeyGen routes configured');
5456
}
5557

0 commit comments

Comments
 (0)