From 196ae2a4481b5c47965f8028f48a55a5decd3503 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 17 Feb 2015 02:20:04 -0300 Subject: [PATCH] add test --- lib/client/api.js | 87 ++++++++++++--------- test/integration/clientApi.js | 121 ++++++++++++++++++----------- test/integration/clienttestdata.js | 92 ++++++++++++++++++++++ 3 files changed, 218 insertions(+), 82 deletions(-) create mode 100644 test/integration/clienttestdata.js diff --git a/lib/client/api.js b/lib/client/api.js index 4aa554a..a15e01d 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -62,35 +62,49 @@ function API(opts) { }; +API.prototype._isWalletCorrupt = function(wallet, data) { + var pubKey = Bitcore.PrivateKey.fromString(data.walletPrivKey).toPublicKey().toString(); + var fake = []; + + if (data.n != wallet.copayers.length) + return true; + + var uniq = []; + _.each(wallet.copayers, function(copayer) { + if (uniq[copayer.xPubKey]++) + return true; + + if (!SignUtils.verify(copayer.xPubKey, copayer.xPubKeySignature, pubKey)) { + fake.push(copayer); + } + }); + return fake.length > 0; +}; + + API.prototype._tryToComplete = function(data, cb) { var self = this; - var isCorrupted; var url = '/v1/wallets/'; self._doGetRequest(url, data, function(err, wallet) { if (err) return cb(err); - if (wallet.n > 0 && wallet.status === 'complete' && !data.verified) { - var pubKey = Bitcore.PrivateKey.fromString(data.walletPrivKey).toPublicKey().toString(); - var fake = []; - _.each(wallet.copayers, function(copayer) { - if (!SignUtils.verify(copayer.xPubKey, copayer.xPubKeySignature, pubKey)) { - fake.push(copayer); - } - }); - if (fake.length > 0) { - isCorrupted = true; + + if (wallet.status === 'complete' && !data.verified) { + + if (self._isWalletCorrupt(wallet, data)) { data.verified = 'corrupt'; } else { data.verified = 'ok'; } self.storage.save(data, function(err) { - if (isCorrupted) { + if (data.verified == 'corrupt') { return cb('Some copayers in the wallet could not be verified to have known the wallet secret'); } return cb(err, data); }); + } else { + return cb('Wallet Incomplete'); } - return cb(null, data); }); }; @@ -98,12 +112,12 @@ API.prototype._tryToComplete = function(data, cb) { API.prototype._loadAndCheck = function(cb) { - var self = this; + var self = this; + this.storage.load(function(err, data) { if (err || !data) { return cb(err || 'Wallet file not found.'); } - if (data.verified == 'corrupt') { return cb('The wallet is tagged as corrupt. Some of the copayers cannot be verified to have known the wallet secret.'); } @@ -131,10 +145,13 @@ API.prototype._doRequest = function(method, url, args, data, cb) { body: args, json: true, }; - log.verbose('Request Args', util.inspect(args)); + log.verbose('Request Args', util.inspect(args, { + depth: 10 + })); this.request(args, function(err, res, body) { - log.verbose('Response:', err, body); - + log.verbose(util.inspect(body, { + depth: 10 + })); if (err) return cb(err); if (res.statusCode != 200) { _parseError(body); @@ -279,34 +296,32 @@ API.prototype.getStatus = function(cb) { API.prototype.sendTxProposal = function(inArgs, cb) { var self = this; - this._loadAndCheck( - function(err, data) { - if (err) return cb(err); + this._loadAndCheck(function(err, data) { + if (err) return cb(err); - var args = _createProposalOpts(inArgs, data.signingPrivKey); + var args = _createProposalOpts(inArgs, data.signingPrivKey); - var url = '/v1/txproposals/'; - self._doPostRequest(url, args, data, cb); - }); + var url = '/v1/txproposals/'; + self._doPostRequest(url, args, data, cb); + }); }; API.prototype.createAddress = function(cb) { var self = this; - this._loadAndCheck( - function(err, data) { + this._loadAndCheck(function(err, data) { + if (err) return cb(err); + + var url = '/v1/addresses/'; + self._doPostRequest(url, {}, data, function(err, address) { if (err) return cb(err); + if (!Verifier.checkAddress(data, address)) { + return cb(new ServerCompromisedError('Server sent fake address')); + } - var url = '/v1/addresses/'; - self._doPostRequest(url, {}, data, function(err, address) { - if (err) return cb(err); - if (!Verifier.checkAddress(data, address)) { - return cb(new ServerCompromisedError('Server sent fake address')); - } - - return cb(null, address); - }); + return cb(null, address); }); + }); }; API.prototype.history = function(limit, cb) { diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 12fe14b..6d52304 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -7,51 +7,95 @@ var should = chai.should(); var Client = require('../../lib/client'); var API = Client.API; var Bitcore = require('bitcore'); +var TestData = require('./clienttestdata'); -var wallet11 = { - "m": 1, - "n": 1, - "walletPrivKey": "{\"bn\":\"6b862ffbfc90a37a2fedbbcfea91c6a4e49f49b6aaa322b6e16c46bfdbe71a38\",\"compressed\":true,\"network\":\"livenet\"}", - "network": "testnet", - "xPrivKey": "tprv8ZgxMBicQKsPeisyNJteQXZnb7CnhYc4TVAyxxicXuxMjK1rmaqVq1xnXtbSTPxUKKL9h5xJhUvw1AKfDD3i98A82eJWSYRWYjmPksewFKR", - "copayerId": "a84daa08-17b5-45ad-84cd-e275f3b07123", - "signingPrivKey": "42798f82c4ed9ace4d66335165071edf180e70bc0fc08dacb3e35185a2141d5b", - "publicKeyRing": ["tpubD6NzVbkrYhZ4YBumFxZEowDuA8iirsny2nmmFUkuxBkkZoGdPyf61Waei3tDYvVa1yqW82Xhmmd6oiibeDyM1MS3zTiky7Yg75UEV9oQhFJ"] -}; - -var incompleteWallet22 = { - "m": 2, - "n": 2, - "walletPrivKey": "L3XSE3KNjQM1XRP1h5yMCSKsN4hs3D6eK7Vwn5M88Bs6jpCnXR3R", - "network": "testnet", - "secret": "d9cf45a1-6793-4df4-94df-c99d2c2e1fe9:bc2488c1b83e455a4b908a0d0aeaf70351efc48fbcaa454bffefdef419a5ee6a:T", - "xPrivKey": "tprv8ZgxMBicQKsPdoC5DGtnXx7fp7YnUtGv8b7fU2oDQfDpHFQh1QCgpKc8GHpdsBN5THaHYMV5LgD5cP5NYaacGVr786p3mVLSZff9berTV8h", - "copayerId": "c3a33ca0-37cf-4e80-b745-71272683835c", - "signingPrivKey": "6e129c4996666e5ecdf78aed626c01977fa19eacce6659738ebe065f86523e9b", - "publicKeyRing": [] -}; - - -describe('client API', function() { +describe(' client API ', function() { var client; beforeEach(function() { var fsmock = {};; - fsmock.readFile = sinon.mock().yields(null, JSON.stringify(wallet11)); + fsmock.readFile = sinon.mock().yields(null, JSON.stringify(TestData.storage.wallet11)); fsmock.writeFile = sinon.mock().yields(); var storage = new Client.FileStorage({ filename: 'dummy', fs: fsmock, }); client = new Client({ - storage: storage, + storage: storage }); }); - describe('createAddress', function() { - it('should check address', function(done) { + describe(' _tryToComplete ', function() { + it('should complete a wallet ', function(done) { + var request = sinon.stub(); + + // Wallet request + request.onCall(0).yields(null, { + statusCode: 200, + }, TestData.serverResponse.completeWallet); + request.onCall(1).yields(null, { + statusCode: 200, + }, "pepe"); + + client.request = request; + client.storage.fs.readFile = sinon.stub().yields(null, JSON.stringify(TestData.storage.incompleteWallet22)); + client.getBalance(function(err, x) { + should.not.exist(err); + done(); + }); + }) + + + it('should complain wallet is not complete ', function(done) { + var request = sinon.stub(); + + // Wallet request + request.onCall(0).yields(null, { + statusCode: 200, + }, TestData.serverResponse.incompleteWallet); + + client.request = request; + client.storage.fs.readFile = sinon.stub().yields(null, JSON.stringify(TestData.storage.incompleteWallet22)); + client.createAddress(function(err, x) { + err.should.contain('Incomplete'); + done(); + }); + }) + + it(' should reject wallets with bad signatures', function(done) { + var request = sinon.stub(); + // Wallet request + request.onCall(0).yields(null, { + statusCode: 200, + }, TestData.serverResponse.corruptWallet22); + + client.request = request; + client.storage.fs.readFile = sinon.stub().yields(null, JSON.stringify(TestData.storage.incompleteWallet22)); + client.createAddress(function(err, x) { + err.should.contain('verified'); + done(); + }); + }) + it(' should reject wallets with missing signatures ', function(done) { + var request = sinon.stub(); + // Wallet request + request.onCall(0).yields(null, { + statusCode: 200, + }, TestData.serverResponse.corruptWallet222); + + client.request = request; + client.storage.fs.readFile = sinon.stub().yields(null, JSON.stringify(TestData.storage.incompleteWallet22)); + client.createAddress(function(err, x) { + err.should.contain('verified'); + done(); + }); + }) + }); + + describe(' createAddress ', function() { + it(' should check address ', function(done) { var response = { createdOn: 1424105995, @@ -71,7 +115,7 @@ describe('client API', function() { done(); }); }) - it('should detect fake addresses', function(done) { + it(' should detect fake addresses ', function(done) { var response = { createdOn: 1424105995, address: '2N3fA6wDtnebzywPkGuNK9KkFaEzgbPRRTq', @@ -88,20 +132,5 @@ describe('client API', function() { done(); }); }) - - it('should complain wallet is not complete', function(done) { - var request = sinon.mock().yields(null, { - statusCode: 200 - }, { - dummy: true - }); - client.request = request; - client.storage.fs.readFile = sinon.mock().yields(null, JSON.stringify(incompleteWallet22)); - client.createAddress(function(err, x) { - err.should.contain('Incomplete'); - done(); - }); - }) - - }); + }) }); diff --git a/test/integration/clienttestdata.js b/test/integration/clienttestdata.js new file mode 100644 index 0000000..175658c --- /dev/null +++ b/test/integration/clienttestdata.js @@ -0,0 +1,92 @@ +var storage = { + wallet11: { + "m": 1, + "n": 1, + "walletPrivKey": "{\"bn\":\"6b862ffbfc90a37a2fedbbcfea91c6a4e49f49b6aaa322b6e16c46bfdbe71a38\",\"compressed\":true,\"network\":\"livenet\"}", + "network": "testnet", + "xPrivKey": "tprv8ZgxMBicQKsPeisyNJteQXZnb7CnhYc4TVAyxxicXuxMjK1rmaqVq1xnXtbSTPxUKKL9h5xJhUvw1AKfDD3i98A82eJWSYRWYjmPksewFKR", + "copayerId": "a84daa08-17b5-45ad-84cd-e275f3b07123", + "signingPrivKey": "42798f82c4ed9ace4d66335165071edf180e70bc0fc08dacb3e35185a2141d5b", + "publicKeyRing": ["tpubD6NzVbkrYhZ4YBumFxZEowDuA8iirsny2nmmFUkuxBkkZoGdPyf61Waei3tDYvVa1yqW82Xhmmd6oiibeDyM1MS3zTiky7Yg75UEV9oQhFJ"] + }, + + incompleteWallet22: { + "m": 2, + "n": 2, + "walletPrivKey":"L2Fu6TM1AqSNBaQcjgjvYjGf3EzS3MVSTwEeTw3bvy52x7ZkffWj", + "network": "testnet", + "secret": "b6f57154-0df8-4845-a61d-47ecd648c2d4:eab5a55d9214845ee8d13ea1033e42ec8d7f780ae6e521d830252a80433e91a5:T", + "xPrivKey": "tprv8ZgxMBicQKsPfFVXegcKyJjy2Y5DSrHNrtGBHG1f9pPX75QQdHwHGjWUtR7cCUXV7QcCCDon4cieHWTYscy8M7oXwF3qd3ssfBiV9M68bPB", + "copayerId": "3fc03e7a-6ebc-409b-a4b7-45b14d5a8199", + "signingPrivKey": "0d3c796fb12e387c4b5a5c566312b2b22fa0553ca041d859e3f0987215ca3a4f", + "publicKeyRing": [] + } +}; + +var serverResponse = { + completeWallet: { + m: 2, + n: 2, + status: 'complete', + publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' + ], + addressIndex: 0, + copayers: [{ + xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', + }, { + xPubKey: 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV', + xPubKeySignature: '3044022025c93b418ebdbb66a0f2b21af709420e8ae769bf054f29aaa252cb5417c46a2302205e0c8b931324736b7eea4971a48039614e19abe26e13ab0ef1547aef92b55aab', + }], + pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', + network: 'testnet', + }, + + + incompleteWallet: { + m: 2, + n: 2, + status: 'pending', + publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' + ], + addressIndex: 0, + copayers: [{ + xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', + }], + pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', + network: 'testnet', + }, + + corruptWallet22: { + m: 2, + n: 2, + status: 'complete', + publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' + ], + copayers: [{ + xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', + }, { + xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + xPubKeySignature: 'bababa', + }], + }, + corruptWallet222: { + m: 2, + n: 2, + status: 'complete', + publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' + ], + copayers: [{ + xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + }, ], + } +}; + +module.exports.serverResponse = serverResponse; +module.exports.storage = storage;