@bitpay-labs/bitcore-wallet-service 11.11.1 → 11.11.2
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/routes/wallets.ts +3 -1
- package/src/lib/server.ts +136 -88
- package/src/lib/storage.ts +90 -20
- 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 +144 -0
- package/test/integration/addressReads.test.ts +503 -0
- package/test/integration/bcmonitor.test.ts +44 -0
- package/test/integration/server.test.ts +198 -48
- package/test/integration/walletReads.test.ts +488 -0
- package/ts_build/package.json +3 -3
- 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/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 +1 -1
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +128 -94
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +17 -10
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +73 -20
- package/ts_build/src/lib/storage.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 +132 -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 +181 -48
- package/ts_build/test/integration/server.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/chain/eth.test.ts
CHANGED
|
@@ -19,6 +19,76 @@ describe('Chain ETH', function() {
|
|
|
19
19
|
x.address.should.equal('0x01');
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
describe('#onTx', function() {
|
|
23
|
+
const token = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
24
|
+
const sender = '0xC877cBCF020A8AA259A1Efab1559B2A3A7259086';
|
|
25
|
+
const recipient = '0x37D3bCDA9d7d5Dc41e32DAd77a5BA89a77aA8BD0';
|
|
26
|
+
const multisig = '0x9527a556f0D9AA753dE0D1f9Ad024a17dd281B1D';
|
|
27
|
+
// a tx event as bitcore-node emits it to the wallets room
|
|
28
|
+
const tx = (fields = {}) => ({ chain: 'ETH', network: 'mainnet', txid: '0x01', to: token, from: sender, value: 0, ...fields });
|
|
29
|
+
// effects as bitcore-node builds them
|
|
30
|
+
const erc20 = { type: 'ERC20:transfer', to: recipient, from: sender, amount: '1000000', contractAddress: token, callStack: '' };
|
|
31
|
+
const internal = { to: recipient, from: sender, amount: '2000', callStack: '0' };
|
|
32
|
+
const multisigEffect = type => ({ type, to: recipient, from: sender, amount: '3000', contractAddress: multisig, callStack: '' });
|
|
33
|
+
|
|
34
|
+
it('notifies the tx recipient and value for a tx without effects', function() {
|
|
35
|
+
ChainService.onTx('eth', tx({ to: recipient, value: 5000 })).should.deep.equal({
|
|
36
|
+
txid: '0x01',
|
|
37
|
+
out: { address: recipient, amount: 5000, tokenAddress: undefined, multisigContractAddress: undefined }
|
|
38
|
+
});
|
|
39
|
+
ChainService.onTx('eth', tx({ to: recipient, value: 5000, effects: [] })).out.should.deep.equal({
|
|
40
|
+
address: recipient, amount: 5000, tokenAddress: undefined, multisigContractAddress: undefined
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('notifies the recipient of an internal value transfer', function() {
|
|
45
|
+
ChainService.onTx('eth', tx({ effects: [internal] })).out.should.deep.equal({
|
|
46
|
+
address: recipient, amount: '2000', tokenAddress: undefined, multisigContractAddress: undefined
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
for (const type of ['MULTISIG:confirmTransaction', 'MULTISIG:submitTransaction']) {
|
|
51
|
+
it(`notifies the recipient of a ${type} with its multisig contract`, function() {
|
|
52
|
+
ChainService.onTx('eth', tx({ effects: [internal, multisigEffect(type)] })).out.should.deep.equal({
|
|
53
|
+
address: recipient, amount: '3000', tokenAddress: undefined, multisigContractAddress: multisig
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
it('prefers a multisig effect to an ERC20 transfer', function() {
|
|
59
|
+
ChainService.onTx('eth', tx({ effects: [erc20, multisigEffect('MULTISIG:submitTransaction')] })).out.should.deep.equal({
|
|
60
|
+
address: recipient, amount: '3000', tokenAddress: undefined, multisigContractAddress: multisig
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('falls back to the tx recipient for effects it does not know', function() {
|
|
65
|
+
const other = { type: 'OTHER:call', to: recipient, from: sender, amount: '1', contractAddress: token, callStack: '' };
|
|
66
|
+
ChainService.onTx('eth', tx({ effects: [other] })).out.should.deep.equal({
|
|
67
|
+
address: token, amount: 0, tokenAddress: undefined, multisigContractAddress: undefined
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('handles MATIC txs the same way', function() {
|
|
72
|
+
ChainService.onTx('matic', tx({ chain: 'MATIC', to: recipient, value: 5000 })).out.should.deep.equal({
|
|
73
|
+
address: recipient, amount: 5000, tokenAddress: undefined, multisigContractAddress: undefined
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('notifies the token recipient, amount and token of an ERC20 transfer', function() {
|
|
78
|
+
ChainService.onTx('eth', tx({ effects: [erc20] })).should.deep.equal({
|
|
79
|
+
txid: '0x01',
|
|
80
|
+
out: { address: recipient, amount: '1000000', tokenAddress: token, multisigContractAddress: undefined }
|
|
81
|
+
});
|
|
82
|
+
ChainService.onTx('matic', tx({ chain: 'MATIC', effects: [erc20] })).out.tokenAddress.should.equal(token);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('prefers an ERC20 transfer to an internal value transfer', function() {
|
|
86
|
+
ChainService.onTx('eth', tx({ effects: [internal, erc20] })).out.should.deep.equal({
|
|
87
|
+
address: recipient, amount: '1000000', tokenAddress: token, multisigContractAddress: undefined
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
22
92
|
describe('#selectTxInputs', function() {
|
|
23
93
|
it('should reject a total that a number rounds down to the balance', function(done) {
|
|
24
94
|
const server = {
|
package/test/chain/sol.test.ts
CHANGED
|
@@ -27,7 +27,7 @@ describe('Chain SOL', function() {
|
|
|
27
27
|
getRentMinimum: (_space, cb) => cb(null, Defaults.MIN_SOL_BALANCE)
|
|
28
28
|
}),
|
|
29
29
|
getPendingTxs: (_opts, cb) => cb(null, c.txps),
|
|
30
|
-
storage: {
|
|
30
|
+
storage: { fetchAddressesPage: (_walletId, _opts, cb) => cb(null, []) }
|
|
31
31
|
};
|
|
32
32
|
const wallet = { chain: 'sol', network: 'livenet' };
|
|
33
33
|
|
package/test/chain/xrp.test.ts
CHANGED
|
@@ -20,7 +20,7 @@ describe('Chain XRP', function() {
|
|
|
20
20
|
getReserve: cb => cb(null, 1000000)
|
|
21
21
|
}),
|
|
22
22
|
getPendingTxs: (_opts, cb) => cb(null, [{ amount: '3000000', fee: '12' }]),
|
|
23
|
-
storage: {
|
|
23
|
+
storage: { fetchAddressesPage: (_walletId, _opts, cb) => cb(null, []) }
|
|
24
24
|
};
|
|
25
25
|
const wallet = { chain: 'xrp', network: 'livenet' };
|
|
26
26
|
|
package/test/expressapp.test.ts
CHANGED
|
@@ -11,6 +11,8 @@ 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';
|
|
14
16
|
|
|
15
17
|
const Defaults = Common.Defaults;
|
|
16
18
|
const should = chai.should();
|
|
@@ -762,6 +764,148 @@ describe('ExpressApp', function() {
|
|
|
762
764
|
});
|
|
763
765
|
});
|
|
764
766
|
|
|
767
|
+
describe('/v1/wallets/exist', function() {
|
|
768
|
+
const idA = 'a'.repeat(64);
|
|
769
|
+
const idB = 'b'.repeat(64);
|
|
770
|
+
const idC = 'c'.repeat(64);
|
|
771
|
+
let storage;
|
|
772
|
+
|
|
773
|
+
function sign(message) {
|
|
774
|
+
const hash = Common.Utils.hashMessage(message, false);
|
|
775
|
+
return BitcoreLib.crypto.ECDSA.sign(hash, new BitcoreLib.PrivateKey(TestData.keyPair.priv), { endian: 'little' }).toString();
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// posts each body in turn, and calls back with each response's status and body
|
|
779
|
+
function postEach(bodies, cb) {
|
|
780
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
781
|
+
sandbox.stub(WalletService, 'getStorage').returns(storage);
|
|
782
|
+
start(ExpressApp, function() {
|
|
783
|
+
const results = [];
|
|
784
|
+
(function next() {
|
|
785
|
+
if (results.length === bodies.length) {
|
|
786
|
+
return cb(null, results);
|
|
787
|
+
}
|
|
788
|
+
request({
|
|
789
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/wallets/exist',
|
|
790
|
+
method: 'post',
|
|
791
|
+
json: true,
|
|
792
|
+
body: bodies[results.length]
|
|
793
|
+
}, function(err, res, body) {
|
|
794
|
+
if (err) {
|
|
795
|
+
return cb(err);
|
|
796
|
+
}
|
|
797
|
+
results.push({ status: res.statusCode, body });
|
|
798
|
+
next();
|
|
799
|
+
});
|
|
800
|
+
})();
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function post(body, cb) {
|
|
805
|
+
postEach([body], (err, results) => (err ? cb(err) : cb(null, { statusCode: results[0].status }, results[0].body)));
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
beforeEach(function() {
|
|
809
|
+
const copayer = { requestPubKeys: [{ key: TestData.keyPair.pub }] };
|
|
810
|
+
storage = {
|
|
811
|
+
fetchCopayerLookup: sinon.stub().callsFake(async id => (id === idA || id === idB ? copayer : undefined))
|
|
812
|
+
};
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('returns the copayers whose signature verifies against their request key', function(done) {
|
|
816
|
+
post({
|
|
817
|
+
copayers: [
|
|
818
|
+
{ copayerId: idA, signature: sign(idA) },
|
|
819
|
+
{ copayerId: idB, signature: sign(idA) },
|
|
820
|
+
{ copayerId: idC, signature: sign(idC) }
|
|
821
|
+
]
|
|
822
|
+
}, function(err, res, body) {
|
|
823
|
+
should.not.exist(err);
|
|
824
|
+
res.statusCode.should.equal(200);
|
|
825
|
+
body.should.deep.equal([idA]);
|
|
826
|
+
storage.fetchCopayerLookup.args.should.deep.equal([[idA], [idB], [idC]]);
|
|
827
|
+
done();
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
it('returns no copayers when given none to check', function(done) {
|
|
832
|
+
postEach([
|
|
833
|
+
{},
|
|
834
|
+
{ copayers: null },
|
|
835
|
+
{ copayers: idA },
|
|
836
|
+
{ copayers: { copayerId: idA, signature: sign(idA) } },
|
|
837
|
+
{ copayers: [] }
|
|
838
|
+
], function(err, results) {
|
|
839
|
+
should.not.exist(err);
|
|
840
|
+
results.should.deep.equal(Array(5).fill({ status: 200, body: [] }));
|
|
841
|
+
storage.fetchCopayerLookup.callCount.should.equal(0);
|
|
842
|
+
done();
|
|
843
|
+
});
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
it('does not verify a copayer whose signature is not a string', function(done) {
|
|
847
|
+
post({
|
|
848
|
+
copayers: [
|
|
849
|
+
{ copayerId: idA, signature: 1 },
|
|
850
|
+
{ copayerId: idA, signature: [sign(idA)] },
|
|
851
|
+
{ copayerId: idB, signature: {} },
|
|
852
|
+
{ copayerId: idB }
|
|
853
|
+
]
|
|
854
|
+
}, function(err, res, body) {
|
|
855
|
+
should.not.exist(err);
|
|
856
|
+
res.statusCode.should.equal(200);
|
|
857
|
+
body.should.deep.equal([]);
|
|
858
|
+
storage.fetchCopayerLookup.callCount.should.equal(4);
|
|
859
|
+
done();
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('treats a copayer that fails to load as not found', function(done) {
|
|
864
|
+
storage.fetchCopayerLookup.withArgs(idB).rejects(new Error('lookup failed'));
|
|
865
|
+
post({
|
|
866
|
+
copayers: [
|
|
867
|
+
{ copayerId: idA, signature: sign(idA) },
|
|
868
|
+
{ copayerId: idB, signature: sign(idB) }
|
|
869
|
+
]
|
|
870
|
+
}, function(err, res, body) {
|
|
871
|
+
should.not.exist(err);
|
|
872
|
+
res.statusCode.should.equal(200);
|
|
873
|
+
body.should.deep.equal([idA]);
|
|
874
|
+
done();
|
|
875
|
+
});
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it('looks up only copayer ids that are strings', function(done) {
|
|
879
|
+
post({
|
|
880
|
+
copayers: [
|
|
881
|
+
{ copayerId: { id: idB }, signature: sign(idB) },
|
|
882
|
+
{ copayerId: [idB], signature: sign(idB) },
|
|
883
|
+
{ copayerId: 1, signature: sign(idB) },
|
|
884
|
+
{ signature: sign(idB) },
|
|
885
|
+
{ copayerId: idA, signature: sign(idA) }
|
|
886
|
+
]
|
|
887
|
+
}, function(err, res, body) {
|
|
888
|
+
should.not.exist(err);
|
|
889
|
+
res.statusCode.should.equal(200);
|
|
890
|
+
body.should.deep.equal([idA]);
|
|
891
|
+
storage.fetchCopayerLookup.args.should.deep.equal([[idA]]);
|
|
892
|
+
done();
|
|
893
|
+
});
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it('skips copayers that are not objects', function(done) {
|
|
897
|
+
post({
|
|
898
|
+
copayers: [null, idB, { copayerId: idA, signature: sign(idA) }]
|
|
899
|
+
}, function(err, res, body) {
|
|
900
|
+
should.not.exist(err);
|
|
901
|
+
res.statusCode.should.equal(200);
|
|
902
|
+
body.should.deep.equal([idA]);
|
|
903
|
+
storage.fetchCopayerLookup.args.should.deep.equal([[idA]]);
|
|
904
|
+
done();
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
|
|
765
909
|
describe('Clear cache', function() {
|
|
766
910
|
it('/v1/clearcache/', function(done) {
|
|
767
911
|
const resolveStub = sinon.stub().callsFake( () => { return Promise.resolve(true);});
|