@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.
- package/Dockerfile +1 -1
- package/package.json +5 -5
- package/src/lib/chain/btc/index.ts +3 -2
- package/src/lib/chain/eth/index.ts +134 -117
- package/src/lib/chain/sol/index.ts +1 -1
- package/src/lib/chain/xrp/index.ts +1 -1
- package/src/lib/common/constants.ts +1 -1
- package/src/lib/common/utils.ts +18 -1
- package/src/lib/model/tsskeygen.ts +16 -0
- package/src/lib/routes/maintenance.ts +11 -2
- package/src/lib/routes/middleware/authTssRequest.ts +28 -16
- package/src/lib/routes/middleware/verifyTssMessage.ts +4 -1
- package/src/lib/routes/services.ts +7 -1
- package/src/lib/routes/tss.ts +33 -30
- package/src/lib/routes/wallets.ts +3 -1
- package/src/lib/server.ts +198 -105
- package/src/lib/storage.ts +129 -20
- package/src/lib/tss.ts +111 -7
- package/test/chain/eth.test.ts +70 -0
- package/test/chain/sol.test.ts +1 -1
- package/test/chain/xrp.test.ts +1 -1
- package/test/expressapp.test.ts +243 -0
- package/test/integration/addressReads.test.ts +503 -0
- package/test/integration/bcmonitor.test.ts +44 -0
- package/test/integration/server.test.ts +251 -49
- package/test/integration/tss.test.ts +428 -0
- package/test/integration/walletReads.test.ts +488 -0
- package/ts_build/package.json +4 -4
- package/ts_build/src/lib/chain/btc/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/btc/index.js +3 -2
- package/ts_build/src/lib/chain/btc/index.js.map +1 -1
- package/ts_build/src/lib/chain/eth/index.d.ts +8 -1
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +114 -102
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/chain/sol/index.js +1 -1
- package/ts_build/src/lib/chain/sol/index.js.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.js +1 -1
- 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/index.d.ts +2 -1
- package/ts_build/src/lib/common/index.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.d.ts +1 -0
- package/ts_build/src/lib/common/utils.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.js +16 -1
- package/ts_build/src/lib/common/utils.js.map +1 -1
- package/ts_build/src/lib/model/tsskeygen.d.ts +10 -0
- package/ts_build/src/lib/model/tsskeygen.d.ts.map +1 -1
- package/ts_build/src/lib/model/tsskeygen.js +3 -0
- package/ts_build/src/lib/model/tsskeygen.js.map +1 -1
- package/ts_build/src/lib/routes/maintenance.d.ts.map +1 -1
- package/ts_build/src/lib/routes/maintenance.js +12 -4
- package/ts_build/src/lib/routes/maintenance.js.map +1 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts +6 -2
- package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts.map +1 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.js +21 -14
- package/ts_build/src/lib/routes/middleware/authTssRequest.js.map +1 -1
- package/ts_build/src/lib/routes/middleware/verifyTssMessage.d.ts.map +1 -1
- package/ts_build/src/lib/routes/middleware/verifyTssMessage.js +2 -1
- package/ts_build/src/lib/routes/middleware/verifyTssMessage.js.map +1 -1
- package/ts_build/src/lib/routes/services.d.ts.map +1 -1
- package/ts_build/src/lib/routes/services.js +6 -1
- package/ts_build/src/lib/routes/services.js.map +1 -1
- package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
- package/ts_build/src/lib/routes/tss.js +32 -30
- 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 +2 -1
- package/ts_build/src/lib/routes/wallets.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +24 -3
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +148 -97
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +26 -10
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +99 -20
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/src/lib/tss.d.ts +12 -0
- package/ts_build/src/lib/tss.d.ts.map +1 -1
- package/ts_build/src/lib/tss.js +64 -5
- package/ts_build/src/lib/tss.js.map +1 -1
- package/ts_build/test/chain/eth.test.js +59 -0
- package/ts_build/test/chain/eth.test.js.map +1 -1
- package/ts_build/test/chain/sol.test.js +1 -1
- package/ts_build/test/chain/sol.test.js.map +1 -1
- package/ts_build/test/chain/xrp.test.js +1 -1
- package/ts_build/test/chain/xrp.test.js.map +1 -1
- package/ts_build/test/expressapp.test.js +222 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/addressReads.test.d.ts +2 -0
- package/ts_build/test/integration/addressReads.test.d.ts.map +1 -0
- package/ts_build/test/integration/addressReads.test.js +465 -0
- package/ts_build/test/integration/addressReads.test.js.map +1 -0
- package/ts_build/test/integration/bcmonitor.test.js +40 -0
- package/ts_build/test/integration/bcmonitor.test.js.map +1 -1
- package/ts_build/test/integration/server.test.js +228 -48
- 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 +344 -0
- package/ts_build/test/integration/tss.test.js.map +1 -1
- package/ts_build/test/integration/walletReads.test.d.ts +2 -0
- package/ts_build/test/integration/walletReads.test.d.ts.map +1 -0
- package/ts_build/test/integration/walletReads.test.js +436 -0
- package/ts_build/test/integration/walletReads.test.js.map +1 -0
package/test/expressapp.test.ts
CHANGED
|
@@ -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);});
|