This commit is contained in:
Matias Alejo Garcia 2015-02-17 02:20:04 -03:00
parent d56dcd41de
commit 196ae2a448
3 changed files with 218 additions and 82 deletions

View File

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

View File

@ -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();
});
})
});
})
});

View File

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