@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.
Files changed (103) hide show
  1. package/package.json +5 -5
  2. package/src/externalservices/moonpay.ts +6 -2
  3. package/src/lib/common/constants.ts +1 -1
  4. package/src/lib/errors/errordefinitions.ts +8 -0
  5. package/src/lib/model/tsskeygen.ts +12 -4
  6. package/src/lib/routes/middleware/authRequest.ts +6 -16
  7. package/src/lib/routes/middleware/authTssRequest.ts +130 -26
  8. package/src/lib/routes/tss.ts +21 -8
  9. package/src/lib/routes/wallets.ts +8 -9
  10. package/src/lib/server.ts +9 -17
  11. package/src/lib/storage.ts +55 -22
  12. package/src/lib/tss.ts +77 -1
  13. package/test/integration/externalservices/moonpay.test.ts +50 -0
  14. package/test/integration/tss.test.ts +706 -371
  15. package/test/storage.test.ts +6 -7
  16. package/ts_build/package.json +4 -4
  17. package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
  18. package/ts_build/src/externalservices/moonpay.js +8 -3
  19. package/ts_build/src/externalservices/moonpay.js.map +1 -1
  20. package/ts_build/src/lib/chain/btc/index.js +7 -7
  21. package/ts_build/src/lib/chain/btc/index.js.map +1 -1
  22. package/ts_build/src/lib/chain/eth/index.js +1 -1
  23. package/ts_build/src/lib/chain/eth/index.js.map +1 -1
  24. package/ts_build/src/lib/chain/sol/index.d.ts +2 -0
  25. package/ts_build/src/lib/chain/sol/index.d.ts.map +1 -1
  26. package/ts_build/src/lib/chain/sol/index.js +36 -3
  27. package/ts_build/src/lib/chain/sol/index.js.map +1 -1
  28. package/ts_build/src/lib/chain/xrp/index.js +2 -2
  29. package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
  30. package/ts_build/src/lib/common/constants.d.ts +1 -1
  31. package/ts_build/src/lib/common/constants.js +1 -1
  32. package/ts_build/src/lib/common/defaults.d.ts +3 -0
  33. package/ts_build/src/lib/common/defaults.d.ts.map +1 -1
  34. package/ts_build/src/lib/common/defaults.js +3 -0
  35. package/ts_build/src/lib/common/defaults.js.map +1 -1
  36. package/ts_build/src/lib/common/index.d.ts +4 -1
  37. package/ts_build/src/lib/common/index.d.ts.map +1 -1
  38. package/ts_build/src/lib/errors/errordefinitions.d.ts +6 -0
  39. package/ts_build/src/lib/errors/errordefinitions.d.ts.map +1 -1
  40. package/ts_build/src/lib/errors/errordefinitions.js +6 -0
  41. package/ts_build/src/lib/errors/errordefinitions.js.map +1 -1
  42. package/ts_build/src/lib/model/tsskeygen.d.ts +2 -0
  43. package/ts_build/src/lib/model/tsskeygen.d.ts.map +1 -1
  44. package/ts_build/src/lib/model/tsskeygen.js +6 -2
  45. package/ts_build/src/lib/model/tsskeygen.js.map +1 -1
  46. package/ts_build/src/lib/model/txproposal.d.ts +3 -2
  47. package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
  48. package/ts_build/src/lib/model/txproposal.js +33 -9
  49. package/ts_build/src/lib/model/txproposal.js.map +1 -1
  50. package/ts_build/src/lib/routes/context.d.ts.map +1 -1
  51. package/ts_build/src/lib/routes/context.js +3 -1
  52. package/ts_build/src/lib/routes/context.js.map +1 -1
  53. package/ts_build/src/lib/routes/helpers/error.js +2 -2
  54. package/ts_build/src/lib/routes/helpers/error.js.map +1 -1
  55. package/ts_build/src/lib/routes/middleware/authRequest.d.ts.map +1 -1
  56. package/ts_build/src/lib/routes/middleware/authRequest.js +6 -16
  57. package/ts_build/src/lib/routes/middleware/authRequest.js.map +1 -1
  58. package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts +2 -1
  59. package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts.map +1 -1
  60. package/ts_build/src/lib/routes/middleware/authTssRequest.js +104 -24
  61. package/ts_build/src/lib/routes/middleware/authTssRequest.js.map +1 -1
  62. package/ts_build/src/lib/routes/transactions.js +10 -5
  63. package/ts_build/src/lib/routes/transactions.js.map +1 -1
  64. package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
  65. package/ts_build/src/lib/routes/tss.js +19 -7
  66. package/ts_build/src/lib/routes/tss.js.map +1 -1
  67. package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
  68. package/ts_build/src/lib/routes/wallets.js +12 -11
  69. package/ts_build/src/lib/routes/wallets.js.map +1 -1
  70. package/ts_build/src/lib/server.d.ts.map +1 -1
  71. package/ts_build/src/lib/server.js +14 -20
  72. package/ts_build/src/lib/server.js.map +1 -1
  73. package/ts_build/src/lib/storage.d.ts +9 -2
  74. package/ts_build/src/lib/storage.d.ts.map +1 -1
  75. package/ts_build/src/lib/storage.js +51 -20
  76. package/ts_build/src/lib/storage.js.map +1 -1
  77. package/ts_build/src/lib/tss.d.ts +8 -0
  78. package/ts_build/src/lib/tss.d.ts.map +1 -1
  79. package/ts_build/src/lib/tss.js +59 -1
  80. package/ts_build/src/lib/tss.js.map +1 -1
  81. package/ts_build/test/chain/btc.test.js +26 -0
  82. package/ts_build/test/chain/btc.test.js.map +1 -1
  83. package/ts_build/test/chain/eth.test.js +40 -0
  84. package/ts_build/test/chain/eth.test.js.map +1 -1
  85. package/ts_build/test/chain/sol.test.js +156 -0
  86. package/ts_build/test/chain/sol.test.js.map +1 -1
  87. package/ts_build/test/chain/xrp.test.js +18 -0
  88. package/ts_build/test/chain/xrp.test.js.map +1 -1
  89. package/ts_build/test/integration/externalservices/moonpay.test.js +67 -0
  90. package/ts_build/test/integration/externalservices/moonpay.test.js.map +1 -1
  91. package/ts_build/test/integration/server.test.js +87 -0
  92. package/ts_build/test/integration/server.test.js.map +1 -1
  93. package/ts_build/test/integration/tss.test.d.ts.map +1 -1
  94. package/ts_build/test/integration/tss.test.js +599 -357
  95. package/ts_build/test/integration/tss.test.js.map +1 -1
  96. package/ts_build/test/model/txproposal.test.js +101 -0
  97. package/ts_build/test/model/txproposal.test.js.map +1 -1
  98. package/ts_build/test/numberformat-routes.test.d.ts +2 -0
  99. package/ts_build/test/numberformat-routes.test.d.ts.map +1 -0
  100. package/ts_build/test/numberformat-routes.test.js +116 -0
  101. package/ts_build/test/numberformat-routes.test.js.map +1 -0
  102. package/ts_build/test/storage.test.js +6 -7
  103. package/ts_build/test/storage.test.js.map +1 -1
@@ -288,27 +288,19 @@ export class Storage {
288
288
  );
289
289
  }
290
290
 
291
- fetchCopayerLookup(copayerId: string, cb: (err?: any, copayer?: Copayer) => void) {
292
- this.db.collection(collections.COPAYERS_LOOKUP).findOne(
293
- {
294
- copayerId
295
- },
296
- (err, result) => {
297
- if (err) return cb(err);
298
- if (!result) return cb();
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
- return cb(null, Copayer.fromObj(result));
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 storeTssKeyGenParticipant({ id, partyId, copayerId }: { id: string; partyId: number; copayerId: string }) {
1810
- return this.db.collection(collections.TSS_KEYGEN).updateOne(
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
- await storage.storeTssKeyGenParticipant({ id, partyId: message.partyId, copayerId });
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); },