Skip to content

Commit e27c016

Browse files
committed
feat(ebe, mbe): add mpcv2 recovery support for eth-like coins
Ticket: WP-5168
1 parent 58428fe commit e27c016

File tree

20 files changed

+4451
-2661
lines changed

20 files changed

+4451
-2661
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@
2525
"@api-ts/response": "^2.1.0",
2626
"@api-ts/superagent-wrapper": "^1.3.3",
2727
"@api-ts/typed-express-router": "^1.1.13",
28-
"@bitgo/sdk-core": "^35.3.0",
2928
"@bitgo-beta/sdk-lib-mpc": "8.2.1-alpha.291",
3029
"@bitgo/sdk-coin-ada": "^4.11.5",
3130
"@bitgo/sdk-coin-dot": "^4.3.5",
3231
"@bitgo/sdk-coin-sui": "^5.15.5",
3332
"@bitgo/sdk-coin-near": "^2.7.0",
3433
"@bitgo/sdk-coin-sol": "^4.12.5",
35-
"bitgo": "^48.1.0",
3634
"@bitgo/abstract-utxo": "^9.21.4",
35+
"@bitgo/sdk-core": "^35.3.0",
3736
"@bitgo/statics": "^54.6.0",
37+
"@ethereumjs/tx": "^3.3.0",
38+
"bitgo": "^48.1.0",
3839
"body-parser": "^1.20.3",
3940
"connect-timeout": "^1.9.0",
4041
"debug": "^3.1.0",
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { AppMode, EnclavedConfig, TlsMode } from '../../../initConfig';
2+
import { app as enclavedApp } from '../../../enclavedApp';
3+
4+
import express from 'express';
5+
import nock from 'nock';
6+
import 'should';
7+
import * as request from 'supertest';
8+
import * as sinon from 'sinon';
9+
import * as configModule from '../../../initConfig';
10+
import * as bitgoSdk from '@bitgo/sdk-core';
11+
12+
describe('recoveryMpcV2', () => {
13+
let cfg: EnclavedConfig;
14+
let app: express.Application;
15+
let agent: request.SuperAgentTest;
16+
17+
let hdTree: bitgoSdk.Ed25519BIP32;
18+
let MPC: bitgoSdk.Ecdsa;
19+
let bitgoGpgPubKey: string;
20+
21+
// test config
22+
const kmsUrl = 'http://kms.invalid';
23+
const coin = 'tsol';
24+
const accessToken = 'test-token';
25+
26+
// sinon stubs
27+
let configStub: sinon.SinonStub;
28+
29+
before(async () => {
30+
// nock config
31+
nock.disableNetConnect();
32+
nock.enableNetConnect('127.0.0.1');
33+
34+
// app config
35+
cfg = {
36+
appMode: AppMode.ENCLAVED,
37+
port: 0, // Let OS assign a free port
38+
bind: 'localhost',
39+
timeout: 60000,
40+
logFile: '',
41+
kmsUrl: kmsUrl,
42+
tlsMode: TlsMode.DISABLED,
43+
mtlsRequestCert: false,
44+
allowSelfSigned: true,
45+
};
46+
47+
configStub = sinon.stub(configModule, 'initConfig').returns(cfg);
48+
49+
// app setup
50+
app = enclavedApp(cfg);
51+
agent = request.agent(app);
52+
53+
MPC = new bitgoSdk.Ecdsa();
54+
bitgoGpgPubKey =
55+
'-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
56+
'\n' +
57+
'xk8EZo2rshMFK4EEAAoCAwQC6HQa7PXiX2nnpZr/asCcEbgCOcjsR8gcSI8v\n' +
58+
'vMADk59KsFweg+kIzCR3UqfMe2uG6JHwOYpvDREHp/hqtA+hzQViaXRnb8KM\n' +
59+
'BBATCAA+BYJmjauyBAsJBwgJkDwRkYkILA84AxUICgQWAAIBAhkBApsDAh4B\n' +
60+
'FiEEtIZR46psznKbhpKePBGRiQgsDzgAAFehAP4qQ7mRYbDwaBY3Xja36kZQ\n' +
61+
's8vMajrfnesfwXCArF72KQEAoSMkjXtpWWjMbRHMVXFy0EstWqNg7m0FlCGh\n' +
62+
'BsceQZ3OUwRmjauyEgUrgQQACgIDBMHCYxr6G1SaNSiqUpO5BqhZxjQN6355\n' +
63+
'7/p9X36+eKwTKmFFQVecDQrQvIalKc2WoqKxKgCvBSRlOJbBNsxaNN0DAQgH\n' +
64+
'wngEGBMIACoFgmaNq7IJkDwRkYkILA84ApsMFiEEtIZR46psznKbhpKePBGR\n' +
65+
'iQgsDzgAAN/+AQCKM7sRdSRKEkF3vGBSBaqMMAolcK9iujaqkZ/phjNTYwEA\n' +
66+
'mFiLGavuPlAgSCknFZJ0xrrtlLXeWTMjWGU1gsS5Pfo=\n' +
67+
'=7uRX\n' +
68+
'-----END PGP PUBLIC KEY BLOCK-----\n';
69+
});
70+
71+
afterEach(() => {
72+
nock.cleanAll();
73+
});
74+
75+
after(() => {
76+
configStub.restore();
77+
});
78+
79+
it('should be sign a Mpc V2 Recovery', async () => {
80+
const user = await MPC.keyShare(1, 2, 3);
81+
const backup = await MPC.keyShare(2, 2, 3);
82+
const bitgo = await MPC.keyShare(3, 2, 3);
83+
84+
const userSigningMaterial = {
85+
uShare: user.pShare,
86+
bitgoYShare: bitgo.nShares[1],
87+
backupYShare: backup.nShares[1],
88+
};
89+
90+
const backupSigningMaterial = {
91+
uShare: backup.pShare,
92+
bitgoYShare: bitgo.nShares[2],
93+
userYShare: user.nShares[2],
94+
};
95+
96+
const mockKmsUserResponse = {
97+
prv: JSON.stringify(userSigningMaterial),
98+
pub: '030030196a0a8559459029725c5494d4f7110746b27c94d3cd91ff84085ec15f50166f045f5eb1776c65ef7e9c4ffd9979b6bdc6f19e35110c8223b0bac9c0b074',
99+
source: 'user',
100+
type: 'tss',
101+
};
102+
103+
const mockKmsBackupResponse = {
104+
prv: JSON.stringify(backupSigningMaterial),
105+
pub: '030030196a0a8559459029725c5494d4f7110746b27c94d3cd91ff84085ec15f50166f045f5eb1776c65ef7e9c4ffd9979b6bdc6f19e35110c8223b0bac9c0b074',
106+
source: 'backup',
107+
type: 'tss',
108+
};
109+
110+
const input = {
111+
txHex: '02f839824268018502540be4008504a817c8008307a12094a94584f774b9b3e932fd4f1802f3b9aa292bd17188013fbe85edc90000823078c0808080',
112+
pub: '030030196a0a8559459029725c5494d4f7110746b27c94d3cd91ff84085ec15f50166f045f5eb1776c65ef7e9c4ffd9979b6bdc6f19e35110c8223b0bac9c0b074',
113+
}
114+
// nocks for KMS responses
115+
nock(kmsUrl)
116+
.get(`/key/${input}`)
117+
.query({ source: 'user' })
118+
.reply(200, mockKmsUserResponse);
119+
120+
nock(kmsUrl)
121+
.get(`/key/${input}`)
122+
.query({ source: 'backup' })
123+
.reply(200, mockKmsBackupResponse);
124+
125+
const signatureResponse = await agent
126+
.post(`/api/${coin}/mpcv2/recovery/signature`)
127+
.set('Authorization', `Bearer ${accessToken}`)
128+
.send(input);
129+
130+
signatureResponse.status.should.equal(200);
131+
signatureResponse.body.should.have.property('signature');
132+
signatureResponse.body.signature.should.have.property('recid');
133+
signatureResponse.body.signature.should.have.property('r');
134+
signatureResponse.body.signature.should.have.property('s');
135+
signatureResponse.body.signature.should.have.property('y');
136+
signatureResponse.body.should.have.property('txHex');
137+
signatureResponse.body.txHex.should.equal(input.txHex);
138+
});
139+
});
140+
141+
142+
143+

0 commit comments

Comments
 (0)