From 03adc38897f444528744678c204fd7d04521c679 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Tue, 17 Feb 2015 11:48:19 -0300 Subject: [PATCH] add tests, refactor checks --- lib/client/Verifier.js | 46 ++++++++++++++++++++++++++ lib/client/api.js | 52 +++++++----------------------- test/integration/clientApi.js | 23 +++++++++++-- test/integration/clienttestdata.js | 32 +++++++++++++++--- 4 files changed, 106 insertions(+), 47 deletions(-) diff --git a/lib/client/Verifier.js b/lib/client/Verifier.js index 580468b..42ae592 100644 --- a/lib/client/Verifier.js +++ b/lib/client/Verifier.js @@ -1,7 +1,14 @@ var $ = require('preconditions').singleton(); var _ = require('lodash'); +var log = require('npmlog'); +var Bitcore = require('bitcore'); var BitcoinUtils = require('../bitcoinutils') +var SignUtils = require('../signutils'); + +/* + * Checks data given by the server + */ function Verifier(opts) {}; @@ -10,4 +17,43 @@ Verifier.checkAddress = function(data, address) { return (local.address == address.address && JSON.stringify(local.publicKeys) == JSON.stringify(address.publicKeys)); }; + +// + +Verifier.checkCopayers = function(copayers, walletPrivKey, myXPrivKey, n) { + + var walletPubKey = Bitcore.PrivateKey.fromString(walletPrivKey).toPublicKey().toString(); + + if (copayers.length != n) { + log.error('Missing public keys in server response'); + return false; + } + + // Repeated xpub kes? + var uniq = []; + var error; + _.each(copayers, function(copayer) { + if (uniq[copayers.xPubKey]++) { + log.error('Repeated public keys in server response'); + error = true; + } + + // Not signed pub keys + if (!SignUtils.verify(copayer.xPubKey, copayer.xPubKeySignature, walletPubKey)) { + log.error('Invalid signatures in server response'); + error = true; + } + }); + if (error) + return false; + + var myXPubKey = new Bitcore.HDPublicKey(myXPrivKey).toString(); + if (!_.contains(_.pluck(copayers, 'xPubKey'), myXPubKey)) { + log.error('Server response does not contains our public keys') + return false; + } + return true; +}; + + module.exports = Verifier; diff --git a/lib/client/api.js b/lib/client/api.js index a15e01d..066308d 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -62,55 +62,30 @@ 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 url = '/v1/wallets/'; - self._doGetRequest(url, data, function(err, wallet) { + self._doGetRequest(url, data, function(err, ret) { if (err) return cb(err); + var wallet = ret.wallet; - 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 (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 { + if (wallet.status != 'complete') return cb('Wallet Incomplete'); - } + + if (!Verifier.checkCopayers(wallet.copayers, data.walletPrivKey, data.xPrivKey, data.n)) + return cb('Some copayers in the wallet could not be verified to have known the wallet secret'); + + data.publicKeyRing = _.pluck(wallet.copayers, 'xPubKey') + + self.storage.save(data, function(err) { + return cb(err, data); + }); }); }; - - API.prototype._loadAndCheck = function(cb) { var self = this; @@ -118,9 +93,6 @@ API.prototype._loadAndCheck = function(cb) { 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.'); - } if (data.n > 1) { var pkrComplete = data.publicKeyRing && data.m && data.publicKeyRing.length === data.n; diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 6d52304..07f29fd 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -48,7 +48,7 @@ describe(' client API ', function() { }) - it('should complain wallet is not complete ', function(done) { + it('should handle incomple wallets', function(done) { var request = sinon.stub(); // Wallet request @@ -64,7 +64,7 @@ describe(' client API ', function() { }); }) - it(' should reject wallets with bad signatures', function(done) { + it('should reject wallets with bad signatures', function(done) { var request = sinon.stub(); // Wallet request request.onCall(0).yields(null, { @@ -78,7 +78,7 @@ describe(' client API ', function() { done(); }); }) - it(' should reject wallets with missing signatures ', function(done) { + it('should reject wallets with missing signatures ', function(done) { var request = sinon.stub(); // Wallet request request.onCall(0).yields(null, { @@ -92,6 +92,23 @@ describe(' client API ', function() { done(); }); }) + + it('should reject wallets missing caller"s pubkey', function(done) { + var request = sinon.stub(); + // Wallet request + request.onCall(0).yields(null, { + statusCode: 200, + }, TestData.serverResponse.missingMyPubKey); + + 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() { diff --git a/test/integration/clienttestdata.js b/test/integration/clienttestdata.js index 175658c..1b7f018 100644 --- a/test/integration/clienttestdata.js +++ b/test/integration/clienttestdata.js @@ -25,6 +25,27 @@ var storage = { var serverResponse = { completeWallet: { + wallet: { + m: 2, + n: 2, + status: 'complete', + publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' + ], + addressIndex: 0, + copayers: [{ + xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', + xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', + }, { + xPubKey: 'tpubD6NzVbkrYhZ4YiXKYLGvNiQ5bZb9cBUHSBrxZn3xa6BuwZfBFgksTE8M4ZFBLWVJ4PLnAJs2JKhkpJVqsrJEAkGpb62rx62Bk4o4N5Lz8dQ', + xPubKeySignature: '3045022100e03b069db333428153c306c9bf66ebc7f25e7d7f3d087e1ca7234fbbb1a47efa02207421fb375d0dd7a7f2116301f2cdf1bce88554a6c88a82d4ec9fb37fb6680ae8', + }], + pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', + network: 'testnet', + }}, + + missingMyPubKey: { + wallet: { m: 2, n: 2, status: 'complete', @@ -41,10 +62,11 @@ var serverResponse = { }], pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', network: 'testnet', - }, + }}, incompleteWallet: { + wallet: { m: 2, n: 2, status: 'pending', @@ -58,9 +80,10 @@ var serverResponse = { }], pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', network: 'testnet', - }, + }}, corruptWallet22: { + wallet: { m: 2, n: 2, status: 'complete', @@ -74,8 +97,9 @@ var serverResponse = { xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', xPubKeySignature: 'bababa', }], - }, + }}, corruptWallet222: { + wallet: { m: 2, n: 2, status: 'complete', @@ -85,7 +109,7 @@ var serverResponse = { copayers: [{ xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', }, ], - } + }}, }; module.exports.serverResponse = serverResponse;