@bitpay-labs/bitcore-wallet-service 11.10.13 → 11.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/externalservices/moonpay.ts +6 -2
- package/src/lib/common/constants.ts +1 -1
- package/src/lib/errors/errordefinitions.ts +8 -0
- package/src/lib/model/tsskeygen.ts +12 -4
- package/src/lib/routes/middleware/authRequest.ts +6 -16
- package/src/lib/routes/middleware/authTssRequest.ts +130 -26
- package/src/lib/routes/tss.ts +21 -8
- package/src/lib/routes/wallets.ts +8 -9
- package/src/lib/server.ts +9 -17
- package/src/lib/storage.ts +55 -22
- package/src/lib/tss.ts +77 -1
- package/test/integration/externalservices/moonpay.test.ts +50 -0
- package/test/integration/tss.test.ts +706 -371
- package/test/storage.test.ts +6 -7
- package/ts_build/package.json +4 -4
- package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
- package/ts_build/src/externalservices/moonpay.js +8 -3
- package/ts_build/src/externalservices/moonpay.js.map +1 -1
- package/ts_build/src/lib/chain/btc/index.js +7 -7
- package/ts_build/src/lib/chain/btc/index.js.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +1 -1
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/chain/sol/index.d.ts +2 -0
- package/ts_build/src/lib/chain/sol/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/sol/index.js +36 -3
- package/ts_build/src/lib/chain/sol/index.js.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.js +2 -2
- package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
- package/ts_build/src/lib/common/constants.d.ts +1 -1
- package/ts_build/src/lib/common/constants.js +1 -1
- package/ts_build/src/lib/common/defaults.d.ts +3 -0
- package/ts_build/src/lib/common/defaults.d.ts.map +1 -1
- package/ts_build/src/lib/common/defaults.js +3 -0
- package/ts_build/src/lib/common/defaults.js.map +1 -1
- package/ts_build/src/lib/common/index.d.ts +4 -1
- package/ts_build/src/lib/common/index.d.ts.map +1 -1
- package/ts_build/src/lib/errors/errordefinitions.d.ts +6 -0
- package/ts_build/src/lib/errors/errordefinitions.d.ts.map +1 -1
- package/ts_build/src/lib/errors/errordefinitions.js +6 -0
- package/ts_build/src/lib/errors/errordefinitions.js.map +1 -1
- package/ts_build/src/lib/model/tsskeygen.d.ts +2 -0
- package/ts_build/src/lib/model/tsskeygen.d.ts.map +1 -1
- package/ts_build/src/lib/model/tsskeygen.js +6 -2
- package/ts_build/src/lib/model/tsskeygen.js.map +1 -1
- package/ts_build/src/lib/model/txproposal.d.ts +3 -2
- package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposal.js +33 -9
- package/ts_build/src/lib/model/txproposal.js.map +1 -1
- package/ts_build/src/lib/routes/context.d.ts.map +1 -1
- package/ts_build/src/lib/routes/context.js +3 -1
- package/ts_build/src/lib/routes/context.js.map +1 -1
- package/ts_build/src/lib/routes/helpers/error.js +2 -2
- package/ts_build/src/lib/routes/helpers/error.js.map +1 -1
- package/ts_build/src/lib/routes/middleware/authRequest.d.ts.map +1 -1
- package/ts_build/src/lib/routes/middleware/authRequest.js +6 -16
- package/ts_build/src/lib/routes/middleware/authRequest.js.map +1 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts +2 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts.map +1 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.js +104 -24
- package/ts_build/src/lib/routes/middleware/authTssRequest.js.map +1 -1
- package/ts_build/src/lib/routes/transactions.js +10 -5
- package/ts_build/src/lib/routes/transactions.js.map +1 -1
- package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
- package/ts_build/src/lib/routes/tss.js +19 -7
- package/ts_build/src/lib/routes/tss.js.map +1 -1
- package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
- package/ts_build/src/lib/routes/wallets.js +12 -11
- package/ts_build/src/lib/routes/wallets.js.map +1 -1
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +14 -20
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +9 -2
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +51 -20
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/src/lib/tss.d.ts +8 -0
- package/ts_build/src/lib/tss.d.ts.map +1 -1
- package/ts_build/src/lib/tss.js +59 -1
- package/ts_build/src/lib/tss.js.map +1 -1
- package/ts_build/test/chain/btc.test.js +26 -0
- package/ts_build/test/chain/btc.test.js.map +1 -1
- package/ts_build/test/chain/eth.test.js +40 -0
- package/ts_build/test/chain/eth.test.js.map +1 -1
- package/ts_build/test/chain/sol.test.js +156 -0
- package/ts_build/test/chain/sol.test.js.map +1 -1
- package/ts_build/test/chain/xrp.test.js +18 -0
- package/ts_build/test/chain/xrp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/moonpay.test.js +67 -0
- package/ts_build/test/integration/externalservices/moonpay.test.js.map +1 -1
- package/ts_build/test/integration/server.test.js +87 -0
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/integration/tss.test.d.ts.map +1 -1
- package/ts_build/test/integration/tss.test.js +599 -357
- package/ts_build/test/integration/tss.test.js.map +1 -1
- package/ts_build/test/model/txproposal.test.js +101 -0
- package/ts_build/test/model/txproposal.test.js.map +1 -1
- package/ts_build/test/numberformat-routes.test.d.ts +2 -0
- package/ts_build/test/numberformat-routes.test.d.ts.map +1 -0
- package/ts_build/test/numberformat-routes.test.js +116 -0
- package/ts_build/test/numberformat-routes.test.js.map +1 -0
- package/ts_build/test/storage.test.js +6 -7
- package/ts_build/test/storage.test.js.map +1 -1
package/src/lib/storage.ts
CHANGED
|
@@ -288,27 +288,19 @@ export class Storage {
|
|
|
288
288
|
);
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
fetchCopayerLookup(copayerId: string
|
|
292
|
-
this.db.collection(collections.COPAYERS_LOOKUP).findOne(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (!result.requestPubKeys) {
|
|
301
|
-
result.requestPubKeys = [
|
|
302
|
-
{
|
|
303
|
-
key: result.requestPubKey,
|
|
304
|
-
signature: result.signature
|
|
305
|
-
}
|
|
306
|
-
];
|
|
291
|
+
async fetchCopayerLookup(copayerId: string): Promise<Copayer | undefined> {
|
|
292
|
+
const result = await this.db.collection(collections.COPAYERS_LOOKUP).findOne({ copayerId });
|
|
293
|
+
if (!result) return;
|
|
294
|
+
if (!result.requestPubKeys) {
|
|
295
|
+
result.requestPubKeys = [
|
|
296
|
+
{
|
|
297
|
+
key: result.requestPubKey,
|
|
298
|
+
signature: result.signature
|
|
307
299
|
}
|
|
300
|
+
];
|
|
301
|
+
}
|
|
308
302
|
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
);
|
|
303
|
+
return Copayer.fromObj(result);
|
|
312
304
|
}
|
|
313
305
|
|
|
314
306
|
// TODO: should be done client-side
|
|
@@ -1806,16 +1798,48 @@ export class Storage {
|
|
|
1806
1798
|
return this.db.collection(collections.TSS_KEYGEN).insertOne(doc);
|
|
1807
1799
|
}
|
|
1808
1800
|
|
|
1809
|
-
async
|
|
1810
|
-
|
|
1811
|
-
{ id },
|
|
1801
|
+
async storeTssKeyGenAuthPublicKey({ id, partyId, authPublicKey, __v }: { id: string; partyId: number; authPublicKey: string; __v: number }) {
|
|
1802
|
+
const result = await this.db.collection(collections.TSS_KEYGEN).updateOne(
|
|
1803
|
+
{ id, __v },
|
|
1804
|
+
{
|
|
1805
|
+
$set: {
|
|
1806
|
+
[`authPublicKeys.${partyId}`]: authPublicKey
|
|
1807
|
+
},
|
|
1808
|
+
$inc: {
|
|
1809
|
+
__v: 1
|
|
1810
|
+
}
|
|
1811
|
+
},
|
|
1812
|
+
{ upsert: false }
|
|
1813
|
+
);
|
|
1814
|
+
if (!result.matchedCount) {
|
|
1815
|
+
if ((await this.db.collection(collections.TSS_KEYGEN).countDocuments({ id })) === 0) {
|
|
1816
|
+
throw new Error('MONGO_DOC_NOT_FOUND: No document found for id ' + id);
|
|
1817
|
+
}
|
|
1818
|
+
throw new Error('MONGO_DOC_OUTDATED: No document found for version ' + __v);
|
|
1819
|
+
}
|
|
1820
|
+
return result;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
async storeTssKeyGenParticipant({ id, partyId, copayerId, __v }: { id: string; partyId: number; copayerId: string; __v: number }) {
|
|
1824
|
+
const result = await this.db.collection(collections.TSS_KEYGEN).updateOne(
|
|
1825
|
+
{ id, __v },
|
|
1812
1826
|
{
|
|
1813
1827
|
$set: {
|
|
1814
1828
|
[`participants.${partyId}`]: copayerId
|
|
1829
|
+
},
|
|
1830
|
+
$inc: {
|
|
1831
|
+
__v: 1
|
|
1815
1832
|
}
|
|
1816
1833
|
},
|
|
1817
1834
|
{ upsert: false }
|
|
1818
1835
|
);
|
|
1836
|
+
if (!result.matchedCount) {
|
|
1837
|
+
if ((await this.db.collection(collections.TSS_KEYGEN).countDocuments({ id })) === 0) {
|
|
1838
|
+
throw new Error('MONGO_DOC_NOT_FOUND: No document found for id ' + id);
|
|
1839
|
+
}
|
|
1840
|
+
throw new Error('MONGO_DOC_OUTDATED: No document found for version ' + __v);
|
|
1841
|
+
}
|
|
1842
|
+
return result;
|
|
1819
1843
|
}
|
|
1820
1844
|
|
|
1821
1845
|
async storeTssKeyGenMessage({ id, message, __v }: { id: string; message: ITssKeyMessageObject; __v: number }) {
|
|
@@ -1835,6 +1859,9 @@ export class Storage {
|
|
|
1835
1859
|
{ upsert: false }
|
|
1836
1860
|
);
|
|
1837
1861
|
if (!result.matchedCount) {
|
|
1862
|
+
if ((await this.db.collection(collections.TSS_KEYGEN).countDocuments({ id })) === 0) {
|
|
1863
|
+
throw new Error('MONGO_DOC_NOT_FOUND: No document found for id ' + id);
|
|
1864
|
+
}
|
|
1838
1865
|
throw new Error('MONGO_DOC_OUTDATED: No document found for version ' + __v);
|
|
1839
1866
|
}
|
|
1840
1867
|
return result;
|
|
@@ -1902,6 +1929,9 @@ export class Storage {
|
|
|
1902
1929
|
{ upsert: false }
|
|
1903
1930
|
);
|
|
1904
1931
|
if (!result.matchedCount) {
|
|
1932
|
+
if ((await this.db.collection(collections.TSS_SIGN).countDocuments({ id })) === 0) {
|
|
1933
|
+
throw new Error('MONGO_DOC_NOT_FOUND: No document found for id ' + id);
|
|
1934
|
+
}
|
|
1905
1935
|
throw new Error('MONGO_DOC_OUTDATED: No document found for version ' + __v);
|
|
1906
1936
|
}
|
|
1907
1937
|
return result;
|
|
@@ -1924,6 +1954,9 @@ export class Storage {
|
|
|
1924
1954
|
{ upsert: false }
|
|
1925
1955
|
);
|
|
1926
1956
|
if (!result.matchedCount) {
|
|
1957
|
+
if ((await this.db.collection(collections.TSS_SIGN).countDocuments({ id })) === 0) {
|
|
1958
|
+
throw new Error('MONGO_DOC_NOT_FOUND: No document found for id ' + id);
|
|
1959
|
+
}
|
|
1927
1960
|
throw new Error('MONGO_DOC_OUTDATED: No document found for version ' + __v);
|
|
1928
1961
|
}
|
|
1929
1962
|
return result;
|
package/src/lib/tss.ts
CHANGED
|
@@ -155,6 +155,57 @@ class TssKeyGenClass {
|
|
|
155
155
|
return session;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
async registerParty(params: {
|
|
159
|
+
/** Session ID */
|
|
160
|
+
id: string;
|
|
161
|
+
/** Copayer ID of the requesting party */
|
|
162
|
+
copayerId: string;
|
|
163
|
+
/* Copayer data to register */
|
|
164
|
+
data: {
|
|
165
|
+
/** Party ID */
|
|
166
|
+
partyId: string | number;
|
|
167
|
+
/** Public key/Copayer ID of the party */
|
|
168
|
+
partyPubKey: string;
|
|
169
|
+
};
|
|
170
|
+
}) {
|
|
171
|
+
const { id, copayerId, data } = params;
|
|
172
|
+
|
|
173
|
+
const session = await this.getSessionForCopayer({ id, copayerId });
|
|
174
|
+
const reqPartyId = session.participants.indexOf(copayerId);
|
|
175
|
+
if (reqPartyId !== 0) {
|
|
176
|
+
throw Errors.TSS_NON_PARTICIPANT.withMessage('Only the first participant can register a party');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (data.partyId == null || isNaN(data.partyId as number)) {
|
|
180
|
+
throw Errors.TSS_INVALID_PARTY_DATA.withMessage(`Invalid partyId: ${data.partyId}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const partyId = parseInt(data.partyId as string);
|
|
184
|
+
const pubKey = data.partyPubKey?.trim();
|
|
185
|
+
|
|
186
|
+
if (!(partyId > 0) || !pubKey || pubKey === copayerId || partyId >= session.n) {
|
|
187
|
+
throw Errors.TSS_INVALID_PARTY_DATA.withMessage(`Invalid data for party ${partyId} registration`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (session.authPublicKeys.includes(pubKey)) {
|
|
191
|
+
if (session.authPublicKeys.indexOf(pubKey) === partyId) {
|
|
192
|
+
// Already registered at partyId -> return idempotently
|
|
193
|
+
return;
|
|
194
|
+
} else {
|
|
195
|
+
// Trying to re-register a party that is already registered under a different party ID
|
|
196
|
+
throw Errors.TSS_PARTY_ALREADY_REGISTERED;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Has party already submitted initial rounds message? (case when this is a re-register)
|
|
201
|
+
if (session.rounds?.[0]?.some(round => round.fromPartyId === partyId)) {
|
|
202
|
+
throw Errors.TSS_ROUNDS_ALREADY_STARTED.withMessage(`Party ${partyId} has already submitted an initial message. Cannot register a new party ${partyId}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const storage = WalletService.getStorage();
|
|
206
|
+
await storage.storeTssKeyGenAuthPublicKey({ id, partyId, authPublicKey: pubKey, __v: session.__v });
|
|
207
|
+
}
|
|
208
|
+
|
|
158
209
|
/**
|
|
159
210
|
* Get messages for a given party in a TSS keygen session.
|
|
160
211
|
* Only returns messages if all other parties have sent their messages for the round.
|
|
@@ -279,10 +330,29 @@ class TssKeyGenClass {
|
|
|
279
330
|
|
|
280
331
|
|
|
281
332
|
if (!session.participants[message.partyId]) {
|
|
333
|
+
if (session.participants.includes(copayerId)) {
|
|
334
|
+
throw Errors.TSS_PARTY_ALREADY_REGISTERED;
|
|
335
|
+
}
|
|
282
336
|
if (!this._checkPassword({ session, password })) {
|
|
283
337
|
throw Errors.TSS_INVALID_PASSWORD;
|
|
284
338
|
}
|
|
285
|
-
|
|
339
|
+
// Sanity check for the public key of the participant in the session
|
|
340
|
+
// session.authPublicKeys was not added until version 1.2
|
|
341
|
+
if (session.schemeVersion >= 1.2 && session.authPublicKeys[message.partyId] !== message.publicKey) {
|
|
342
|
+
throw Errors.NOT_AUTHORIZED;
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
await storage.storeTssKeyGenParticipant({ id, partyId: message.partyId, copayerId, __v: session.__v });
|
|
346
|
+
} catch (e) {
|
|
347
|
+
if (e?.message?.startsWith('MONGO_DOC_OUTDATED')) {
|
|
348
|
+
return this.processMessage(params);
|
|
349
|
+
} else if (e?.message?.startsWith('MONGO_DOC_NOT_FOUND')) {
|
|
350
|
+
throw Errors.TSS_SESSION_NOT_FOUND;
|
|
351
|
+
}
|
|
352
|
+
throw e;
|
|
353
|
+
}
|
|
354
|
+
} else if (session.participants[message.partyId] !== copayerId) {
|
|
355
|
+
throw Errors.TSS_NON_PARTICIPANT.withMessage('Incorrect copayerId for this partyId');
|
|
286
356
|
}
|
|
287
357
|
|
|
288
358
|
let result = false;
|
|
@@ -427,6 +497,8 @@ class TssKeyGenClass {
|
|
|
427
497
|
} catch (e) {
|
|
428
498
|
if (e?.message?.startsWith('MONGO_DOC_OUTDATED')) {
|
|
429
499
|
return false;
|
|
500
|
+
} else if (e?.message?.startsWith('MONGO_DOC_NOT_FOUND')) {
|
|
501
|
+
throw Errors.TSS_SESSION_NOT_FOUND;
|
|
430
502
|
}
|
|
431
503
|
throw e;
|
|
432
504
|
}
|
|
@@ -694,6 +766,8 @@ class TssSignClass {
|
|
|
694
766
|
} catch (e) {
|
|
695
767
|
if (e?.message?.startsWith('MONGO_DOC_OUTDATED')) {
|
|
696
768
|
return this.processMessage(params);
|
|
769
|
+
} else if (e?.message?.startsWith('MONGO_DOC_NOT_FOUND')) {
|
|
770
|
+
throw Errors.TSS_SESSION_NOT_FOUND;
|
|
697
771
|
}
|
|
698
772
|
throw e;
|
|
699
773
|
}
|
|
@@ -814,6 +888,8 @@ class TssSignClass {
|
|
|
814
888
|
} catch (e) {
|
|
815
889
|
if (e?.message?.startsWith('MONGO_DOC_OUTDATED')) {
|
|
816
890
|
return false;
|
|
891
|
+
} else if (e?.message?.startsWith('MONGO_DOC_NOT_FOUND')) {
|
|
892
|
+
throw Errors.TSS_SESSION_NOT_FOUND;
|
|
817
893
|
}
|
|
818
894
|
throw e;
|
|
819
895
|
}
|
|
@@ -573,6 +573,35 @@ describe('Moonpay integration', () => {
|
|
|
573
573
|
should.exist(data);
|
|
574
574
|
});
|
|
575
575
|
|
|
576
|
+
it('should return the response body if there is one', async () => {
|
|
577
|
+
const data = await server.externalServices.moonpay.moonpayCancelSellTransaction(req);
|
|
578
|
+
data.should.equal('data');
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('should return success true without leaking the response if there is no body', async () => {
|
|
582
|
+
const fakeRequest2 = {
|
|
583
|
+
delete: (_url, opts, _cb) => {
|
|
584
|
+
return _cb(null, { statusCode: 204, body: undefined, headers: {}, request: { headers: opts.headers } });
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
server.externalServices.moonpay.request = fakeRequest2;
|
|
589
|
+
const data = await server.externalServices.moonpay.moonpayCancelSellTransaction(req);
|
|
590
|
+
data.should.deep.equal({ success: true });
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('should return success false if there is no body and status is not 2xx', async () => {
|
|
594
|
+
const fakeRequest2 = {
|
|
595
|
+
delete: (_url, opts, _cb) => {
|
|
596
|
+
return _cb(null, { statusCode: 404, body: '', headers: {}, request: { headers: opts.headers } });
|
|
597
|
+
},
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
server.externalServices.moonpay.request = fakeRequest2;
|
|
601
|
+
const data = await server.externalServices.moonpay.moonpayCancelSellTransaction(req);
|
|
602
|
+
data.should.deep.equal({ success: false });
|
|
603
|
+
});
|
|
604
|
+
|
|
576
605
|
it('should return error if delete returns error', async () => {
|
|
577
606
|
const fakeRequest2 = {
|
|
578
607
|
delete: (_url, _opts, _cb) => { return _cb(new Error('Error'), null); },
|
|
@@ -641,6 +670,27 @@ describe('Moonpay integration', () => {
|
|
|
641
670
|
should.exist(data);
|
|
642
671
|
});
|
|
643
672
|
|
|
673
|
+
it('should return the response body if there is one', async () => {
|
|
674
|
+
const data = await server.externalServices.moonpay.moonpayCreateSession(req);
|
|
675
|
+
data.should.equal('data');
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('should throw without leaking the response if there is no session body', async () => {
|
|
679
|
+
const fakeRequest2 = {
|
|
680
|
+
post: (_url, opts, _cb) => {
|
|
681
|
+
return _cb(null, { statusCode: 200, body: undefined, headers: {}, request: { headers: opts.headers } });
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
server.externalServices.moonpay.request = fakeRequest2;
|
|
686
|
+
try {
|
|
687
|
+
await server.externalServices.moonpay.moonpayCreateSession(req);
|
|
688
|
+
should.fail('should have thrown');
|
|
689
|
+
} catch (err) {
|
|
690
|
+
err.message.should.equal('Moonpay did not return a session');
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
644
694
|
it('should return error if post returns error', async () => {
|
|
645
695
|
const fakeRequest2 = {
|
|
646
696
|
post: (_url, _opts, _cb) => { return _cb(new Error('Error'), null); },
|