diff --git a/.gitignore b/.gitignore index 465aa8a..34b12be 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ node_modules .lock-wscript *.swp -out/ \ No newline at end of file +out/ +db/ \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 6ac2dd3..bf07053 100644 --- a/lib/server.js +++ b/lib/server.js @@ -88,7 +88,8 @@ CopayServer.prototype.createWallet = function(opts, cb) { var self = this, pubKey; - if (!Utils.checkRequired(opts, ['name', 'm', 'n', 'pubKey'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['name', 'm', 'n', 'pubKey'])) + return cb(new ClientError('Required argument missing')); if (_.isEmpty(opts.name)) return cb(new ClientError('Invalid wallet name')); if (!Wallet.verifyCopayerLimits(opts.m, opts.n)) @@ -154,7 +155,8 @@ CopayServer.prototype._verifySignature = function(text, signature, pubKey) { CopayServer.prototype.joinWallet = function(opts, cb) { var self = this; - if (!Utils.checkRequired(opts, ['walletId', 'name', 'xPubKey', 'xPubKeySignature'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['walletId', 'name', 'xPubKey', 'xPubKeySignature'])) + return cb(new ClientError('Required argument missing')); if (_.isEmpty(opts.name)) return cb(new ClientError('Invalid copayer name')); @@ -236,7 +238,8 @@ CopayServer.prototype.getAddresses = function(opts, cb) { CopayServer.prototype.verifyMessageSignature = function(opts, cb) { var self = this; - if (!Utils.checkRequired(opts, ['message', 'signature'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['message', 'signature'])) + return cb(new ClientError('Required argument missing')); self.getWallet({}, function(err, wallet) { if (err) return cb(err); @@ -389,7 +392,8 @@ CopayServer.prototype._selectUtxos = function(txp, utxos) { CopayServer.prototype.createTx = function(opts, cb) { var self = this; - if (!Utils.checkRequired(opts, ['toAddress', 'amount'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['toAddress', 'amount'])) + return cb(new ClientError('Required argument missing')); Utils.runLocked(self.walletId, cb, function(cb) { self.getWallet({}, function(err, wallet) { @@ -496,25 +500,22 @@ CopayServer.prototype.removePendingTx = function(opts, cb) { Utils.runLocked(self.walletId, cb, function(cb) { - self.storage.fetchTx(self.walletId, opts.id, function(err, txp) { + self.getTx({ + id: opts.id + }, function(err, txp) { if (err) return cb(err); - if (!txp) - return cb(new ClientError('Transaction proposal not found')); if (!txp.isPending()) return cb(new ClientError('Transaction proposal not pending')); if (txp.creatorId !== self.copayerId) - return cb(new ClientError('Not allowed to erase this TX')); + return cb(new ClientError('Only creators can remove pending proposals')); var actors = txp.getActors(); - if (actors.length > 1) - return cb(new ClientError('Not allowed to erase this TX')); - - if (actors.length == 1 && actors[0] !== self.copayerId) - return cb(new ClientError('Not allowed to erase this TX')); + if (actors.length > 1 || (actors.length == 1 && actors[0] !== self.copayerId)) + return cb(new ClientError('Cannot remove a proposal signed/rejected by other copayers')); self.storage.removeTx(self.walletId, opts.id, cb); }); @@ -539,7 +540,8 @@ CopayServer.prototype._broadcastTx = function(txp, cb) { CopayServer.prototype.signTx = function(opts, cb) { var self = this; - if (!Utils.checkRequired(opts, ['txProposalId', 'signatures'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['txProposalId', 'signatures'])) + return cb(new ClientError('Required argument missing')); self.getWallet({}, function(err, wallet) { if (err) return cb(err); @@ -548,7 +550,6 @@ CopayServer.prototype.signTx = function(opts, cb) { id: opts.txProposalId }, function(err, txp) { if (err) return cb(err); - if (!txp) return cb(new ClientError('Transaction proposal not found')); var action = _.find(txp.actions, { copayerId: self.copayerId @@ -596,13 +597,14 @@ CopayServer.prototype.signTx = function(opts, cb) { CopayServer.prototype.rejectTx = function(opts, cb) { var self = this; - if (!Utils.checkRequired(opts, ['txProposalId'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['txProposalId'])) + return cb(new ClientError('Required argument missing')); self.getTx({ id: opts.txProposalId }, function(err, txp) { if (err) return cb(err); - if (!txp) return cb(new ClientError('Transaction proposal not found')); + var action = _.find(txp.actions, { copayerId: self.copayerId }); diff --git a/lib/utils.js b/lib/utils.js index 905d798..b39fb7a 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -7,6 +7,8 @@ var Utils = {}; Utils.runLocked = function(token, cb, task) { var self = this; + $.shouldBeDefined(token); + Lock.get(token, function(lock) { var _cb = function() { cb.apply(null, arguments); diff --git a/test/integration.js b/test/integration.js index 1f632a3..9e2aa30 100644 --- a/test/integration.js +++ b/test/integration.js @@ -104,7 +104,7 @@ helpers.createUtxos = function(server, wallet, amounts, cb) { return { txid: helpers.randomTXID(), vout: Math.floor((Math.random() * 10) + 1), - satoshis: amount, + satoshis: helpers.toSatoshi(amount), scriptPubKey: addresses[i].getScriptPubKey(wallet.m).toBuffer().toString('hex'), address: addresses[i++].address, }; @@ -570,7 +570,7 @@ describe('Copay server', function() { }); it('should create a tx', function(done) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([100, 200]), function(utxos) { + helpers.createUtxos(server, wallet, [100, 200], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -625,7 +625,7 @@ describe('Copay server', function() { }); it('should fail to create tx for address invalid address', function(done) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([100, 200]), function(utxos) { + helpers.createUtxos(server, wallet, [100, 200], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('invalid address', 80, null, copayerPriv[0].privKey); @@ -640,7 +640,7 @@ describe('Copay server', function() { }); it('should fail to create tx for address of different network', function(done) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([100, 200]), function(utxos) { + helpers.createUtxos(server, wallet, [100, 200], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('myE38JHdxmQcTJGP1ZiX4BiGhDxMJDvLJD', 80, null, copayerPriv[0].privKey); @@ -655,7 +655,7 @@ describe('Copay server', function() { }); it('should fail to create tx when insufficient funds', function(done) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([100]), function(utxos) { + helpers.createUtxos(server, wallet, [100], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 120, null, copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -680,7 +680,7 @@ describe('Copay server', function() { it.skip('should fail to create tx for dust amount', function(done) {}); it('should create tx when there is a pending tx and enough UTXOs', function(done) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([10.1, 10.2, 10.3]), function(utxos) { + helpers.createUtxos(server, wallet, [10.1, 10.2, 10.3], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -706,7 +706,7 @@ describe('Copay server', function() { }); it('should fail to create tx when there is a pending tx and not enough UTXOs', function(done) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([10.1, 10.2, 10.3]), function(utxos) { + helpers.createUtxos(server, wallet, [10.1, 10.2, 10.3], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -734,9 +734,9 @@ describe('Copay server', function() { it('should create tx using different UTXOs for simultaneous requests', function(done) { var N = 5; - helpers.createUtxos(server, wallet, helpers.toSatoshi(_.times(N, function() { + helpers.createUtxos(server, wallet, _.times(N, function() { return 100; - })), function(utxos) { + }), function(utxos) { helpers.stubBlockExplorer(server, utxos); server.getBalance({}, function(err, balance) { should.not.exist(err); @@ -776,7 +776,7 @@ describe('Copay server', function() { copayerPriv = c; copayerIds = ids; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([1, 2, 3, 4, 5, 6, 7, 8]), function(utxos) { + helpers.createUtxos(server, wallet, _.range(1, 9), function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -826,7 +826,7 @@ describe('Copay server', function() { copayerPriv = c; copayerIds = ids; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([1, 2, 3, 4, 5, 6, 7, 8]), function(utxos) { + helpers.createUtxos(server, wallet, _.range(1, 9), function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -968,7 +968,7 @@ describe('Copay server', function() { wallet = w; copayerPriv = c; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([1, 2, 3, 4, 5, 6, 7, 8]), function(inutxos) { + helpers.createUtxos(server, wallet, _.range(1, 9), function(inutxos) { utxos = inutxos; done(); }); @@ -1042,7 +1042,7 @@ describe('Copay server', function() { wallet = w; copayerPriv = c; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([1, 2, 3, 4, 5, 6, 7, 8]), function(inutxos) { + helpers.createUtxos(server, wallet, _.range(1, 9), function(inutxos) { utxos = inutxos; done(); }); @@ -1090,7 +1090,7 @@ describe('Copay server', function() { wallet = w; copayerPriv = c; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi(_.range(10)), function(utxos) { + helpers.createUtxos(server, wallet, _.range(10), function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, null, copayerPriv[0].privKey); async.eachSeries(_.range(10), function(i, next) { @@ -1177,7 +1177,7 @@ describe('Copay server', function() { wallet = w; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi(_.range(2)), function(utxos) { + helpers.createUtxos(server, wallet, _.range(2), function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = { toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', @@ -1225,7 +1225,7 @@ describe('Copay server', function() { wallet = w; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi(_.range(2)), function(utxos) { + helpers.createUtxos(server, wallet, _.range(2), function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = { toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', @@ -1261,7 +1261,7 @@ describe('Copay server', function() { wallet = w; copayerPriv = c; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, helpers.toSatoshi([100, 200]), function(utxos) { + helpers.createUtxos(server, wallet, [100, 200], function(utxos) { helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', copayerPriv[0].privKey); server.createTx(txOpts, function(err, tx) { @@ -1310,7 +1310,7 @@ describe('Copay server', function() { server2.removePendingTx({ id: txp.id }, function(err) { - err.message.should.contain('Not allowed'); + err.message.should.contain('creators'); server2.getPendingTxs({}, function(err, txs) { txs.length.should.equal(1); done(); @@ -1330,7 +1330,7 @@ describe('Copay server', function() { server.removePendingTx({ id: txp.id }, function(err) { - err.message.should.contain('Not allowed'); + err.message.should.contain('other copayers'); done(); }); });