add tests, refactor checks
This commit is contained in:
parent
196ae2a448
commit
03adc38897
|
@ -1,7 +1,14 @@
|
||||||
var $ = require('preconditions').singleton();
|
var $ = require('preconditions').singleton();
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
var log = require('npmlog');
|
||||||
|
|
||||||
|
var Bitcore = require('bitcore');
|
||||||
var BitcoinUtils = require('../bitcoinutils')
|
var BitcoinUtils = require('../bitcoinutils')
|
||||||
|
var SignUtils = require('../signutils');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks data given by the server
|
||||||
|
*/
|
||||||
|
|
||||||
function Verifier(opts) {};
|
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));
|
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;
|
module.exports = Verifier;
|
||||||
|
|
|
@ -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) {
|
API.prototype._tryToComplete = function(data, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var url = '/v1/wallets/';
|
var url = '/v1/wallets/';
|
||||||
self._doGetRequest(url, data, function(err, wallet) {
|
self._doGetRequest(url, data, function(err, ret) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
var wallet = ret.wallet;
|
||||||
|
|
||||||
if (wallet.status === 'complete' && !data.verified) {
|
if (wallet.status != 'complete')
|
||||||
|
return cb('Wallet Incomplete');
|
||||||
|
|
||||||
if (self._isWalletCorrupt(wallet, data)) {
|
if (!Verifier.checkCopayers(wallet.copayers, data.walletPrivKey, data.xPrivKey, data.n))
|
||||||
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('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);
|
return cb(err, data);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return cb('Wallet Incomplete');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
API.prototype._loadAndCheck = function(cb) {
|
API.prototype._loadAndCheck = function(cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -118,9 +93,6 @@ API.prototype._loadAndCheck = function(cb) {
|
||||||
if (err || !data) {
|
if (err || !data) {
|
||||||
return cb(err || 'Wallet file not found.');
|
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) {
|
if (data.n > 1) {
|
||||||
var pkrComplete = data.publicKeyRing && data.m && data.publicKeyRing.length === data.n;
|
var pkrComplete = data.publicKeyRing && data.m && data.publicKeyRing.length === data.n;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
var request = sinon.stub();
|
||||||
|
|
||||||
// Wallet request
|
// Wallet request
|
||||||
|
@ -92,6 +92,23 @@ describe(' client API ', function() {
|
||||||
done();
|
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() {
|
describe(' createAddress ', function() {
|
||||||
|
|
|
@ -25,6 +25,27 @@ var storage = {
|
||||||
|
|
||||||
var serverResponse = {
|
var serverResponse = {
|
||||||
completeWallet: {
|
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,
|
m: 2,
|
||||||
n: 2,
|
n: 2,
|
||||||
status: 'complete',
|
status: 'complete',
|
||||||
|
@ -41,10 +62,11 @@ var serverResponse = {
|
||||||
}],
|
}],
|
||||||
pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }',
|
pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }',
|
||||||
network: 'testnet',
|
network: 'testnet',
|
||||||
},
|
}},
|
||||||
|
|
||||||
|
|
||||||
incompleteWallet: {
|
incompleteWallet: {
|
||||||
|
wallet: {
|
||||||
m: 2,
|
m: 2,
|
||||||
n: 2,
|
n: 2,
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
|
@ -58,9 +80,10 @@ var serverResponse = {
|
||||||
}],
|
}],
|
||||||
pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }',
|
pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }',
|
||||||
network: 'testnet',
|
network: 'testnet',
|
||||||
},
|
}},
|
||||||
|
|
||||||
corruptWallet22: {
|
corruptWallet22: {
|
||||||
|
wallet: {
|
||||||
m: 2,
|
m: 2,
|
||||||
n: 2,
|
n: 2,
|
||||||
status: 'complete',
|
status: 'complete',
|
||||||
|
@ -74,8 +97,9 @@ var serverResponse = {
|
||||||
xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E',
|
xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E',
|
||||||
xPubKeySignature: 'bababa',
|
xPubKeySignature: 'bababa',
|
||||||
}],
|
}],
|
||||||
},
|
}},
|
||||||
corruptWallet222: {
|
corruptWallet222: {
|
||||||
|
wallet: {
|
||||||
m: 2,
|
m: 2,
|
||||||
n: 2,
|
n: 2,
|
||||||
status: 'complete',
|
status: 'complete',
|
||||||
|
@ -85,7 +109,7 @@ var serverResponse = {
|
||||||
copayers: [{
|
copayers: [{
|
||||||
xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E',
|
xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E',
|
||||||
}, ],
|
}, ],
|
||||||
}
|
}},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.serverResponse = serverResponse;
|
module.exports.serverResponse = serverResponse;
|
||||||
|
|
Loading…
Reference in New Issue