diff --git a/lib/client/Verifier.js b/lib/client/Verifier.js index 9cc75e4..639b73e 100644 --- a/lib/client/Verifier.js +++ b/lib/client/Verifier.js @@ -54,6 +54,8 @@ Verifier.checkCopayers = function(copayers, walletPrivKey, myXPrivKey, n) { Verifier.checkTxProposal = function(data, txp) { + $.checkArgument(txp.creatorId); + var creatorXPubKey = _.find(data.publicKeyRing, function(xPubKey) { if (WalletUtils.xPubToCopayerId(xPubKey) === txp.creatorId) return true; }); diff --git a/lib/client/api.js b/lib/client/api.js index 4f3e452..3530582 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -449,6 +449,7 @@ API.prototype.getTxProposals = function(opts, cb) { API.prototype.signTxProposal = function(txp, cb) { var self = this; + $.checkArgument(txp.creatorId); this._loadAndCheck(function(err, data) { if (err) return cb(err); @@ -497,6 +498,7 @@ API.prototype.signTxProposal = function(txp, cb) { API.prototype.rejectTxProposal = function(txp, reason, cb) { var self = this; + $.checkArgument(cb); this._loadAndCheck( function(err, data) { diff --git a/lib/server.js b/lib/server.js index 2ae43d0..486e762 100644 --- a/lib/server.js +++ b/lib/server.js @@ -780,7 +780,7 @@ CopayServer.prototype.rejectTx = function(opts, cb) { }); }; - return cb(); + return cb(null, txp); }); }); }; diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 8739c69..16c0288 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -83,14 +83,14 @@ fsmock._get = function(name) { }; -var utxos = []; var blockExplorerMock = {}; +blockExplorerMock.utxos = []; blockExplorerMock.getUnspentUtxos = function(dummy, cb) { - var ret = _.map(utxos || [], function(x) { + var ret = _.map(blockExplorerMock.utxos || [], function(x) { x.toObject = function() { return this; }; @@ -100,17 +100,23 @@ blockExplorerMock.getUnspentUtxos = function(dummy, cb) { }; blockExplorerMock.setUtxo = function(address, amount, m) { - utxos.push({ + blockExplorerMock.utxos.push({ txid: Bitcore.crypto.Hash.sha256(new Buffer(Math.random() * 100000)).toString('hex'), vout: Math.floor((Math.random() * 10) + 1), amount: amount, address: address.address, - scriptPubKey: Bitcore.Script.buildMultisigOut(address.publicKeys, m).toScriptHashOut(), + scriptPubKey: Bitcore.Script.buildMultisigOut(address.publicKeys, m).toScriptHashOut().toString(), }); }; + +blockExplorerMock.broadcast = function(raw, cb) { + blockExplorerMock.lastBroadcasted = raw; + return cb(null, (new Bitcore.Transaction(raw)).id); +}; + blockExplorerMock.reset = function() { - utxos = []; + blockExplorerMock.utxos = []; }; describe('client API ', function() { @@ -355,15 +361,15 @@ describe('client API ', function() { }); - describe.only('Send Transactions', function() { + describe('Send Transactions', function() { it('Send and broadcast in 1-1 wallet', function(done) { helpers.createAndJoinWallet(clients, 1, 1, function(err, w) { clients[0].createAddress(function(err, x0) { should.not.exist(err); should.exist(x0.address); - blockExplorerMock.setUtxo(x0, 10, 1); + blockExplorerMock.setUtxo(x0, 1, 1); var opts = { - amount: 10000, + amount: '0.1btc', toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', message: 'hola 1-1', }; @@ -373,8 +379,10 @@ describe('client API ', function() { x.requiredSignatures.should.equal(1); x.status.should.equal('pending'); x.changeAddress.path.should.equal('m/2147483647/1/0'); - clients[0].signTxProposal(x.id, function(err, res) { - should.not.exist(err, err.message); + clients[0].signTxProposal(x, function(err, tx) { + should.not.exist(err); + tx.status.should.equal('broadcasted'); + tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); done(); }); }); @@ -397,15 +405,91 @@ describe('client API ', function() { x.status.should.equal('pending'); x.requiredRejections.should.equal(2); x.requiredSignatures.should.equal(2); - clients[0].signTxProposal(x.id, function(err, res) { - should.not.exist(err, err.message); - done(); + clients[0].signTxProposal(x, function(err, tx) { + should.not.exist(err, err); + tx.status.should.equal('pending'); + clients[1].signTxProposal(x, function(err, tx) { + should.not.exist(err); + tx.status.should.equal('broadcasted'); + tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); + done(); + }); }); }); }); }); }); - + + it('Send, reject, 2 signs and broadcast in 2-3 wallet', function(done) { + helpers.createAndJoinWallet(clients, 2, 3, function(err, w) { + clients[0].createAddress(function(err, x0) { + should.not.exist(err); + should.exist(x0.address); + blockExplorerMock.setUtxo(x0, 10, 1); + var opts = { + amount: 10000, + toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', + message: 'hola 1-1', + }; + clients[0].sendTxProposal(opts, function(err, x) { + should.not.exist(err); + x.status.should.equal('pending'); + x.requiredRejections.should.equal(2); + x.requiredSignatures.should.equal(2); + clients[0].rejectTxProposal(x, 'no me gusto', function(err, tx) { + should.not.exist(err, err); + tx.status.should.equal('pending'); + clients[1].signTxProposal(x, function(err, tx) { + should.not.exist(err); + clients[2].signTxProposal(x, function(err, tx) { + should.not.exist(err); + tx.status.should.equal('broadcasted'); + tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); + done(); + }); + }); + }); + }); + }); + }); + }); + + it('Send, reject in 3-4 wallet', function(done) { + helpers.createAndJoinWallet(clients, 3, 4, function(err, w) { + clients[0].createAddress(function(err, x0) { + should.not.exist(err); + should.exist(x0.address); + blockExplorerMock.setUtxo(x0, 10, 1); + var opts = { + amount: 10000, + toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', + message: 'hola 1-1', + }; + clients[0].sendTxProposal(opts, function(err, x) { + should.not.exist(err); + x.status.should.equal('pending'); + x.requiredRejections.should.equal(2); + x.requiredSignatures.should.equal(3); + + clients[0].rejectTxProposal(x, 'no me gusto', function(err, tx) { + should.not.exist(err, err); + tx.status.should.equal('pending'); + clients[1].signTxProposal(x, function(err, tx) { + should.not.exist(err); + tx.status.should.equal('pending'); + clients[2].rejectTxProposal(x, 'tampoco me gusto', function(err, tx) { + should.not.exist(err); + tx.status.should.equal('rejected'); + done(); + }); + }); + }); + }); + }); + }); + }); + + });