add tests, refactor checks

This commit is contained in:
Matias Alejo Garcia 2015-02-17 11:48:19 -03:00
parent 196ae2a448
commit 03adc38897
4 changed files with 106 additions and 47 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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() {

View File

@ -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;