@bitpay-labs/bitcore-wallet-service 11.11.1 → 11.11.3

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 (105) hide show
  1. package/Dockerfile +1 -1
  2. package/package.json +5 -5
  3. package/src/lib/chain/btc/index.ts +3 -2
  4. package/src/lib/chain/eth/index.ts +134 -117
  5. package/src/lib/chain/sol/index.ts +1 -1
  6. package/src/lib/chain/xrp/index.ts +1 -1
  7. package/src/lib/common/constants.ts +1 -1
  8. package/src/lib/common/utils.ts +18 -1
  9. package/src/lib/model/tsskeygen.ts +16 -0
  10. package/src/lib/routes/maintenance.ts +11 -2
  11. package/src/lib/routes/middleware/authTssRequest.ts +28 -16
  12. package/src/lib/routes/middleware/verifyTssMessage.ts +4 -1
  13. package/src/lib/routes/services.ts +7 -1
  14. package/src/lib/routes/tss.ts +33 -30
  15. package/src/lib/routes/wallets.ts +3 -1
  16. package/src/lib/server.ts +198 -105
  17. package/src/lib/storage.ts +129 -20
  18. package/src/lib/tss.ts +111 -7
  19. package/test/chain/eth.test.ts +70 -0
  20. package/test/chain/sol.test.ts +1 -1
  21. package/test/chain/xrp.test.ts +1 -1
  22. package/test/expressapp.test.ts +243 -0
  23. package/test/integration/addressReads.test.ts +503 -0
  24. package/test/integration/bcmonitor.test.ts +44 -0
  25. package/test/integration/server.test.ts +251 -49
  26. package/test/integration/tss.test.ts +428 -0
  27. package/test/integration/walletReads.test.ts +488 -0
  28. package/ts_build/package.json +4 -4
  29. package/ts_build/src/lib/chain/btc/index.d.ts.map +1 -1
  30. package/ts_build/src/lib/chain/btc/index.js +3 -2
  31. package/ts_build/src/lib/chain/btc/index.js.map +1 -1
  32. package/ts_build/src/lib/chain/eth/index.d.ts +8 -1
  33. package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
  34. package/ts_build/src/lib/chain/eth/index.js +114 -102
  35. package/ts_build/src/lib/chain/eth/index.js.map +1 -1
  36. package/ts_build/src/lib/chain/sol/index.js +1 -1
  37. package/ts_build/src/lib/chain/sol/index.js.map +1 -1
  38. package/ts_build/src/lib/chain/xrp/index.js +1 -1
  39. package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
  40. package/ts_build/src/lib/common/constants.d.ts +1 -1
  41. package/ts_build/src/lib/common/constants.js +1 -1
  42. package/ts_build/src/lib/common/index.d.ts +2 -1
  43. package/ts_build/src/lib/common/index.d.ts.map +1 -1
  44. package/ts_build/src/lib/common/utils.d.ts +1 -0
  45. package/ts_build/src/lib/common/utils.d.ts.map +1 -1
  46. package/ts_build/src/lib/common/utils.js +16 -1
  47. package/ts_build/src/lib/common/utils.js.map +1 -1
  48. package/ts_build/src/lib/model/tsskeygen.d.ts +10 -0
  49. package/ts_build/src/lib/model/tsskeygen.d.ts.map +1 -1
  50. package/ts_build/src/lib/model/tsskeygen.js +3 -0
  51. package/ts_build/src/lib/model/tsskeygen.js.map +1 -1
  52. package/ts_build/src/lib/routes/maintenance.d.ts.map +1 -1
  53. package/ts_build/src/lib/routes/maintenance.js +12 -4
  54. package/ts_build/src/lib/routes/maintenance.js.map +1 -1
  55. package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts +6 -2
  56. package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts.map +1 -1
  57. package/ts_build/src/lib/routes/middleware/authTssRequest.js +21 -14
  58. package/ts_build/src/lib/routes/middleware/authTssRequest.js.map +1 -1
  59. package/ts_build/src/lib/routes/middleware/verifyTssMessage.d.ts.map +1 -1
  60. package/ts_build/src/lib/routes/middleware/verifyTssMessage.js +2 -1
  61. package/ts_build/src/lib/routes/middleware/verifyTssMessage.js.map +1 -1
  62. package/ts_build/src/lib/routes/services.d.ts.map +1 -1
  63. package/ts_build/src/lib/routes/services.js +6 -1
  64. package/ts_build/src/lib/routes/services.js.map +1 -1
  65. package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
  66. package/ts_build/src/lib/routes/tss.js +32 -30
  67. package/ts_build/src/lib/routes/tss.js.map +1 -1
  68. package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
  69. package/ts_build/src/lib/routes/wallets.js +2 -1
  70. package/ts_build/src/lib/routes/wallets.js.map +1 -1
  71. package/ts_build/src/lib/server.d.ts +24 -3
  72. package/ts_build/src/lib/server.d.ts.map +1 -1
  73. package/ts_build/src/lib/server.js +148 -97
  74. package/ts_build/src/lib/server.js.map +1 -1
  75. package/ts_build/src/lib/storage.d.ts +26 -10
  76. package/ts_build/src/lib/storage.d.ts.map +1 -1
  77. package/ts_build/src/lib/storage.js +99 -20
  78. package/ts_build/src/lib/storage.js.map +1 -1
  79. package/ts_build/src/lib/tss.d.ts +12 -0
  80. package/ts_build/src/lib/tss.d.ts.map +1 -1
  81. package/ts_build/src/lib/tss.js +64 -5
  82. package/ts_build/src/lib/tss.js.map +1 -1
  83. package/ts_build/test/chain/eth.test.js +59 -0
  84. package/ts_build/test/chain/eth.test.js.map +1 -1
  85. package/ts_build/test/chain/sol.test.js +1 -1
  86. package/ts_build/test/chain/sol.test.js.map +1 -1
  87. package/ts_build/test/chain/xrp.test.js +1 -1
  88. package/ts_build/test/chain/xrp.test.js.map +1 -1
  89. package/ts_build/test/expressapp.test.js +222 -0
  90. package/ts_build/test/expressapp.test.js.map +1 -1
  91. package/ts_build/test/integration/addressReads.test.d.ts +2 -0
  92. package/ts_build/test/integration/addressReads.test.d.ts.map +1 -0
  93. package/ts_build/test/integration/addressReads.test.js +465 -0
  94. package/ts_build/test/integration/addressReads.test.js.map +1 -0
  95. package/ts_build/test/integration/bcmonitor.test.js +40 -0
  96. package/ts_build/test/integration/bcmonitor.test.js.map +1 -1
  97. package/ts_build/test/integration/server.test.js +228 -48
  98. package/ts_build/test/integration/server.test.js.map +1 -1
  99. package/ts_build/test/integration/tss.test.d.ts.map +1 -1
  100. package/ts_build/test/integration/tss.test.js +344 -0
  101. package/ts_build/test/integration/tss.test.js.map +1 -1
  102. package/ts_build/test/integration/walletReads.test.d.ts +2 -0
  103. package/ts_build/test/integration/walletReads.test.d.ts.map +1 -0
  104. package/ts_build/test/integration/walletReads.test.js +436 -0
  105. package/ts_build/test/integration/walletReads.test.js.map +1 -0
@@ -11,6 +11,9 @@ import { Common } from '../src/lib/common';
11
11
  import { WalletService } from '../src/lib/server';
12
12
  import { ExpressApp } from '../src/lib/expressapp';
13
13
  import { ClientError } from '../src/lib/errors/clienterror';
14
+ import { BitcoreLib } from '@bitpay-labs/crypto-wallet-core';
15
+ import * as TestData from './testdata';
16
+ import { Stats } from '../src/lib/stats';
14
17
 
15
18
  const Defaults = Common.Defaults;
16
19
  const should = chai.should();
@@ -63,6 +66,104 @@ describe('ExpressApp', function() {
63
66
  httpServer.close();
64
67
  });
65
68
 
69
+ describe('Public query validation', function() {
70
+ let runStats, getFiatRate;
71
+
72
+ beforeEach(function() {
73
+ runStats = sandbox.stub(Stats.prototype, 'run').callsArgWith(0, null, {});
74
+ getFiatRate = sinon.stub().callsArgWith(1, null, {});
75
+ sandbox.stub(WalletService, 'initialize').callsArg(1);
76
+ sandbox.stub(WalletService, 'getInstance').returns({ getFiatRate } as any);
77
+ });
78
+
79
+ const targets = [
80
+ { path: '/v1/stats/', parameter: 'network' },
81
+ { path: '/v1/stats/', parameter: 'coin' },
82
+ { path: '/v1/fiatrates/USD/', parameter: 'coin' }
83
+ ];
84
+
85
+ for (const { path, parameter } of targets) {
86
+ const value = parameter === 'network' ? 'livenet' : 'btc';
87
+ // Cover operator objects, bracket arrays, and duplicate parameters.
88
+ const invalidQueries = [
89
+ `${parameter}[$regex]=.*`,
90
+ `${parameter}[]=${value}`,
91
+ `${parameter}=${value}&${parameter}=${value}`
92
+ ];
93
+ for (const query of invalidQueries) {
94
+ it(`should reject ${path}?${query} before accessing data`, function(done) {
95
+ start(ExpressApp, function() {
96
+ request({ url: testHost + ':' + testPort + config.basePath + path + '?' + query, json: true }, function(err, res, body) {
97
+ should.not.exist(err);
98
+ res.statusCode.should.equal(400);
99
+ body.code.should.equal('BADREQUEST');
100
+ runStats.called.should.equal(false);
101
+ getFiatRate.called.should.equal(false);
102
+ done();
103
+ });
104
+ });
105
+ });
106
+ }
107
+ }
108
+
109
+ for (const query of ['', '?coin=&network=']) {
110
+ it(`should preserve stats defaults for ${query || 'omitted parameters'}`, function(done) {
111
+ start(ExpressApp, function() {
112
+ request({ url: testHost + ':' + testPort + config.basePath + '/v1/stats/' + query, json: true }, function(err, res) {
113
+ should.not.exist(err);
114
+ res.statusCode.should.equal(200);
115
+ runStats.calledOnce.should.equal(true);
116
+ runStats.firstCall.thisValue.coin.should.equal('btc');
117
+ runStats.firstCall.thisValue.network.should.equal('livenet');
118
+ done();
119
+ });
120
+ });
121
+ });
122
+ }
123
+
124
+ it('should pass valid stats filters through unchanged', function(done) {
125
+ start(ExpressApp, function() {
126
+ const query = '?coin=eth&network=testnet&from=2026-01-01&to=2026-02-01';
127
+ request({ url: testHost + ':' + testPort + config.basePath + '/v1/stats/' + query, json: true }, function(err, res) {
128
+ should.not.exist(err);
129
+ res.statusCode.should.equal(200);
130
+ runStats.calledOnce.should.equal(true);
131
+ const stats = runStats.firstCall.thisValue;
132
+ stats.coin.should.equal('eth');
133
+ stats.network.should.equal('testnet');
134
+ stats.from.should.equal('2026-01-01');
135
+ stats.to.should.equal('2026-02-01');
136
+ done();
137
+ });
138
+ });
139
+ });
140
+
141
+ for (const query of ['', '?coin=']) {
142
+ it(`should preserve the fiat coin default for ${query || 'omitted parameters'}`, function(done) {
143
+ start(ExpressApp, function() {
144
+ request({ url: testHost + ':' + testPort + config.basePath + '/v1/fiatrates/USD/' + query, json: true }, function(err, res) {
145
+ should.not.exist(err);
146
+ res.statusCode.should.equal(200);
147
+ getFiatRate.calledOnceWithExactly({ code: 'USD', coin: 'btc', ts: null }, sinon.match.func).should.equal(true);
148
+ done();
149
+ });
150
+ });
151
+ });
152
+ }
153
+
154
+ it('should pass a valid fiat coin and numeric timestamp through unchanged', function(done) {
155
+ start(ExpressApp, function() {
156
+ const query = '?coin=eth&ts=1700000000000';
157
+ request({ url: testHost + ':' + testPort + config.basePath + '/v1/fiatrates/EUR/' + query, json: true }, function(err, res) {
158
+ should.not.exist(err);
159
+ res.statusCode.should.equal(200);
160
+ getFiatRate.calledOnceWithExactly({ code: 'EUR', coin: 'eth', ts: 1700000000000 }, sinon.match.func).should.equal(true);
161
+ done();
162
+ });
163
+ });
164
+ });
165
+ });
166
+
66
167
  it('/v2/wallets', function(done) {
67
168
  const server = {
68
169
  getStatus: sinon.stub().callsArgWith(1, null, {}),
@@ -762,6 +863,148 @@ describe('ExpressApp', function() {
762
863
  });
763
864
  });
764
865
 
866
+ describe('/v1/wallets/exist', function() {
867
+ const idA = 'a'.repeat(64);
868
+ const idB = 'b'.repeat(64);
869
+ const idC = 'c'.repeat(64);
870
+ let storage;
871
+
872
+ function sign(message) {
873
+ const hash = Common.Utils.hashMessage(message, false);
874
+ return BitcoreLib.crypto.ECDSA.sign(hash, new BitcoreLib.PrivateKey(TestData.keyPair.priv), { endian: 'little' }).toString();
875
+ }
876
+
877
+ // posts each body in turn, and calls back with each response's status and body
878
+ function postEach(bodies, cb) {
879
+ sandbox.stub(WalletService, 'initialize').callsArg(1);
880
+ sandbox.stub(WalletService, 'getStorage').returns(storage);
881
+ start(ExpressApp, function() {
882
+ const results = [];
883
+ (function next() {
884
+ if (results.length === bodies.length) {
885
+ return cb(null, results);
886
+ }
887
+ request({
888
+ url: testHost + ':' + testPort + config.basePath + '/v1/wallets/exist',
889
+ method: 'post',
890
+ json: true,
891
+ body: bodies[results.length]
892
+ }, function(err, res, body) {
893
+ if (err) {
894
+ return cb(err);
895
+ }
896
+ results.push({ status: res.statusCode, body });
897
+ next();
898
+ });
899
+ })();
900
+ });
901
+ }
902
+
903
+ function post(body, cb) {
904
+ postEach([body], (err, results) => (err ? cb(err) : cb(null, { statusCode: results[0].status }, results[0].body)));
905
+ }
906
+
907
+ beforeEach(function() {
908
+ const copayer = { requestPubKeys: [{ key: TestData.keyPair.pub }] };
909
+ storage = {
910
+ fetchCopayerLookup: sinon.stub().callsFake(async id => (id === idA || id === idB ? copayer : undefined))
911
+ };
912
+ });
913
+
914
+ it('returns the copayers whose signature verifies against their request key', function(done) {
915
+ post({
916
+ copayers: [
917
+ { copayerId: idA, signature: sign(idA) },
918
+ { copayerId: idB, signature: sign(idA) },
919
+ { copayerId: idC, signature: sign(idC) }
920
+ ]
921
+ }, function(err, res, body) {
922
+ should.not.exist(err);
923
+ res.statusCode.should.equal(200);
924
+ body.should.deep.equal([idA]);
925
+ storage.fetchCopayerLookup.args.should.deep.equal([[idA], [idB], [idC]]);
926
+ done();
927
+ });
928
+ });
929
+
930
+ it('returns no copayers when given none to check', function(done) {
931
+ postEach([
932
+ {},
933
+ { copayers: null },
934
+ { copayers: idA },
935
+ { copayers: { copayerId: idA, signature: sign(idA) } },
936
+ { copayers: [] }
937
+ ], function(err, results) {
938
+ should.not.exist(err);
939
+ results.should.deep.equal(Array(5).fill({ status: 200, body: [] }));
940
+ storage.fetchCopayerLookup.callCount.should.equal(0);
941
+ done();
942
+ });
943
+ });
944
+
945
+ it('does not verify a copayer whose signature is not a string', function(done) {
946
+ post({
947
+ copayers: [
948
+ { copayerId: idA, signature: 1 },
949
+ { copayerId: idA, signature: [sign(idA)] },
950
+ { copayerId: idB, signature: {} },
951
+ { copayerId: idB }
952
+ ]
953
+ }, function(err, res, body) {
954
+ should.not.exist(err);
955
+ res.statusCode.should.equal(200);
956
+ body.should.deep.equal([]);
957
+ storage.fetchCopayerLookup.callCount.should.equal(4);
958
+ done();
959
+ });
960
+ });
961
+
962
+ it('treats a copayer that fails to load as not found', function(done) {
963
+ storage.fetchCopayerLookup.withArgs(idB).rejects(new Error('lookup failed'));
964
+ post({
965
+ copayers: [
966
+ { copayerId: idA, signature: sign(idA) },
967
+ { copayerId: idB, signature: sign(idB) }
968
+ ]
969
+ }, function(err, res, body) {
970
+ should.not.exist(err);
971
+ res.statusCode.should.equal(200);
972
+ body.should.deep.equal([idA]);
973
+ done();
974
+ });
975
+ });
976
+
977
+ it('looks up only copayer ids that are strings', function(done) {
978
+ post({
979
+ copayers: [
980
+ { copayerId: { id: idB }, signature: sign(idB) },
981
+ { copayerId: [idB], signature: sign(idB) },
982
+ { copayerId: 1, signature: sign(idB) },
983
+ { signature: sign(idB) },
984
+ { copayerId: idA, signature: sign(idA) }
985
+ ]
986
+ }, function(err, res, body) {
987
+ should.not.exist(err);
988
+ res.statusCode.should.equal(200);
989
+ body.should.deep.equal([idA]);
990
+ storage.fetchCopayerLookup.args.should.deep.equal([[idA]]);
991
+ done();
992
+ });
993
+ });
994
+
995
+ it('skips copayers that are not objects', function(done) {
996
+ post({
997
+ copayers: [null, idB, { copayerId: idA, signature: sign(idA) }]
998
+ }, function(err, res, body) {
999
+ should.not.exist(err);
1000
+ res.statusCode.should.equal(200);
1001
+ body.should.deep.equal([idA]);
1002
+ storage.fetchCopayerLookup.args.should.deep.equal([[idA]]);
1003
+ done();
1004
+ });
1005
+ });
1006
+ });
1007
+
765
1008
  describe('Clear cache', function() {
766
1009
  it('/v1/clearcache/', function(done) {
767
1010
  const resolveStub = sinon.stub().callsFake( () => { return Promise.resolve(true);});