diff --git a/lib/server.js b/lib/server.js index a93bef0..29b620b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -695,24 +695,9 @@ WalletService.prototype.signTx = function(opts, cb) { self._notify('TxProposalFinallyAccepted', { txProposalId: opts.txProposalId, }); - - self._broadcastTx(txp, function(err, txid) { - if (err) return cb(err); - - txp.setBroadcasted(txid); - self.storage.storeTx(self.walletId, txp, function(err) { - if (err) return cb(err); - - self._notify('NewOutgoingTx', { - txProposalId: opts.txProposalId, - txid: txid - }); - return cb(null, txp); - }); - }); - } else { - return cb(null, txp); } + + return cb(null, txp); }); }); }); @@ -756,7 +741,7 @@ WalletService.prototype.broadcastTx = function(opts, cb) { txid: txid }); - return cb(null, txid); + return cb(null, txp); }); }); }); diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 11d8cbb..aa71dff 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -230,7 +230,7 @@ describe('client API ', function() { clients[i].on('needNewPassword', function(cb) { return cb('1234#$@#%F,./.**'); }); - + }); }); @@ -722,7 +722,7 @@ describe('client API ', function() { clients[0].signTxProposal(txp, function(err, txp) { should.not.exist(err); - txp.status.should.equal('broadcasted'); + txp.status.should.equal('accepted'); done(); }); }); @@ -1141,17 +1141,20 @@ describe('client API ', function() { toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', message: 'hello 1-1', }; - clients[0].sendTxProposal(opts, function(err, x) { + clients[0].sendTxProposal(opts, function(err, txp) { should.not.exist(err); - x.requiredRejections.should.equal(1); - x.requiredSignatures.should.equal(1); - x.status.should.equal('pending'); - x.changeAddress.path.should.equal('m/2147483647/1/0'); - clients[0].signTxProposal(x, function(err, tx) { + txp.requiredRejections.should.equal(1); + txp.requiredSignatures.should.equal(1); + txp.status.should.equal('pending'); + txp.changeAddress.path.should.equal('m/2147483647/1/0'); + clients[0].signTxProposal(txp, function(err, txp) { should.not.exist(err); - tx.status.should.equal('broadcasted'); - tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); - done(); + txp.status.should.equal('accepted'); + clients[0].broadcastTxProposal(txp, function(err, txp) { + txp.status.should.equal('broadcasted'); + txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); + done(); + }); }); }); }); @@ -1168,14 +1171,14 @@ describe('client API ', function() { toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', message: 'hello 1-1', }; - clients[0].sendTxProposal(opts, function(err, x) { + clients[0].sendTxProposal(opts, function(err, txp) { should.not.exist(err); clients[0].getStatus(function(err, st) { should.not.exist(err); - var x = st.pendingTxps[0]; - x.status.should.equal('pending'); - x.requiredRejections.should.equal(2); - x.requiredSignatures.should.equal(2); + var txp = st.pendingTxps[0]; + txp.status.should.equal('pending'); + txp.requiredRejections.should.equal(2); + txp.requiredSignatures.should.equal(2); var w = st.wallet; w.copayers.length.should.equal(3); w.status.should.equal('complete'); @@ -1184,14 +1187,17 @@ describe('client API ', function() { b.lockedAmount.should.equal(1000000000); - clients[0].signTxProposal(x, function(err, tx) { + clients[0].signTxProposal(txp, function(err, txp) { should.not.exist(err, err); - tx.status.should.equal('pending'); - clients[1].signTxProposal(x, function(err, tx) { + txp.status.should.equal('pending'); + clients[1].signTxProposal(txp, function(err, txp) { should.not.exist(err); - tx.status.should.equal('broadcasted'); - tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); - done(); + txp.status.should.equal('accepted'); + clients[1].broadcastTxProposal(txp, function(err, txp) { + txp.status.should.equal('broadcasted'); + txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); + done(); + }); }); }); }); @@ -1211,21 +1217,24 @@ describe('client API ', function() { toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', message: 'hello 1-1', }; - clients[0].sendTxProposal(opts, function(err, x) { + clients[0].sendTxProposal(opts, function(err, txp) { should.not.exist(err); - x.status.should.equal('pending'); - x.requiredRejections.should.equal(2); - x.requiredSignatures.should.equal(2); - clients[0].rejectTxProposal(x, 'wont sign', function(err, tx) { + txp.status.should.equal('pending'); + txp.requiredRejections.should.equal(2); + txp.requiredSignatures.should.equal(2); + clients[0].rejectTxProposal(txp, 'wont sign', function(err, txp) { should.not.exist(err, err); - tx.status.should.equal('pending'); - clients[1].signTxProposal(x, function(err, tx) { + txp.status.should.equal('pending'); + clients[1].signTxProposal(txp, function(err, txp) { should.not.exist(err); - clients[2].signTxProposal(x, function(err, tx) { + clients[2].signTxProposal(txp, function(err, txp) { should.not.exist(err); - tx.status.should.equal('broadcasted'); - tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); - done(); + txp.status.should.equal('accepted'); + clients[2].broadcastTxProposal(txp, function(err, txp) { + txp.status.should.equal('broadcasted'); + txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); + done(); + }); }); }); }); @@ -1245,21 +1254,21 @@ describe('client API ', function() { toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', message: 'hello 1-1', }; - clients[0].sendTxProposal(opts, function(err, x) { + clients[0].sendTxProposal(opts, function(err, txp) { should.not.exist(err); - x.status.should.equal('pending'); - x.requiredRejections.should.equal(2); - x.requiredSignatures.should.equal(3); + txp.status.should.equal('pending'); + txp.requiredRejections.should.equal(2); + txp.requiredSignatures.should.equal(3); - clients[0].rejectTxProposal(x, 'wont sign', function(err, tx) { + clients[0].rejectTxProposal(txp, 'wont sign', function(err, txp) { should.not.exist(err, err); - tx.status.should.equal('pending'); - clients[1].signTxProposal(x, function(err, tx) { + txp.status.should.equal('pending'); + clients[1].signTxProposal(txp, function(err, txp) { should.not.exist(err); - tx.status.should.equal('pending'); - clients[2].rejectTxProposal(x, 'me neither', function(err, tx) { + txp.status.should.equal('pending'); + clients[2].rejectTxProposal(txp, 'me neither', function(err, txp) { should.not.exist(err); - tx.status.should.equal('rejected'); + txp.status.should.equal('rejected'); done(); }); }); @@ -1280,19 +1289,21 @@ describe('client API ', function() { toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', message: 'hello 1-1', }; - clients[0].sendTxProposal(opts, function(err, x) { + clients[0].sendTxProposal(opts, function(err, txp) { should.not.exist(err); - x.status.should.equal('pending'); - x.requiredRejections.should.equal(2); - x.requiredSignatures.should.equal(2); - clients[0].signTxProposal(x, function(err, tx) { - should.not.exist(err, err); - tx.status.should.equal('pending'); - clients[0].signTxProposal(x, function(err, tx) { + txp.status.should.equal('pending'); + txp.requiredRejections.should.equal(2); + txp.requiredSignatures.should.equal(2); + clients[0].signTxProposal(txp, function(err, txp) { + should.not.exist(err); + txp.status.should.equal('pending'); + clients[0].signTxProposal(txp, function(err) { + should.exist(err); err.code.should.contain('CVOTED'); - clients[1].rejectTxProposal(x, 'xx', function(err, tx) { + clients[1].rejectTxProposal(txp, 'xx', function(err, txp) { should.not.exist(err); - clients[1].rejectTxProposal(x, 'xx', function(err, tx) { + clients[1].rejectTxProposal(txp, 'xx', function(err) { + should.exist(err); err.code.should.contain('CVOTED'); done(); }); diff --git a/test/integration/server.js b/test/integration/server.js index a48a4f7..4373b7a 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -1085,85 +1085,6 @@ describe('Copay server', function() { }); }); }); - - }); - - - describe('#signTx and broadcast', function() { - var server, wallet; - beforeEach(function(done) { - helpers.createAndJoinWallet(1, 1, function(s, w) { - server = s; - wallet = w; - helpers.stubUtxos(server, wallet, _.range(1, 9), function() { - done(); - }); - }); - }); - - it('should sign and broadcast a tx', function(done) { - helpers.stubBroadcast('1122334455'); - var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey); - server.createTx(txOpts, function(err, txp) { - should.not.exist(err); - txp.should.exist; - var txpid = txp.id; - - server.getPendingTxs({}, function(err, txps) { - var txp = txps[0]; - txp.id.should.equal(txpid); - var signatures = helpers.clientSign(txp, TestData.copayers[0].xPrivKey); - server.signTx({ - txProposalId: txpid, - signatures: signatures, - }, function(err, txp) { - should.not.exist(err); - txp.status.should.equal('broadcasted'); - txp.txid.should.equal('1122334455'); - server.getTx({ - id: txp.id - }, function(err, txp) { - txp.actions.length.should.equal(1); - txp.actions[0].copayerId.should.equal(wallet.copayers[0].id); - txp.actions[0].copayerName.should.equal(wallet.copayers[0].name); - done(); - }); - }); - }); - }); - }); - - - it('should keep tx as *accepted* if unable to broadcast it', function(done) { - helpers.stubBroadcastFail(); - var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey); - server.createTx(txOpts, function(err, txp) { - should.not.exist(err); - txp.should.exist; - var txpid = txp.id; - - server.getPendingTxs({}, function(err, txps) { - var txp = txps[0]; - txp.id.should.equal(txpid); - var signatures = helpers.clientSign(txp, TestData.copayers[0].xPrivKey); - server.signTx({ - txProposalId: txpid, - signatures: signatures, - }, function(err, txp) { - err.should.contain('broadcast'); - - server.getPendingTxs({}, function(err, txps) { - should.not.exist(err); - txps.length.should.equal(1); - var txp = txps[0]; - txp.status.should.equal('accepted'); - should.not.exist(txp.txid); - done(); - }); - }); - }); - }); - }); }); describe('Tx proposal workflow', function() { @@ -1196,7 +1117,7 @@ describe('Copay server', function() { }); }); - it('tx proposals should not be broadcast until quorum is reached', function(done) { + it('tx proposals should not be finally accepted until quorum is reached', function(done) { var txpId; async.waterfall([ @@ -1234,12 +1155,18 @@ describe('Copay server', function() { txps.length.should.equal(1); var txp = txps[0]; txp.isPending().should.be.true; - txp.isRejected().should.be.false; txp.isAccepted().should.be.false; + txp.isRejected().should.be.false; + txp.isBroadcasted().should.be.false; txp.actions.length.should.equal(1); var action = txp.getActionBy(wallet.copayers[0].id); action.type.should.equal('accept'); - next(null, txp); + server.getNotifications({}, function(err, notifications) { + should.not.exist(err); + var last = _.last(notifications); + last.type.should.not.equal('TxProposalFinallyAccepted'); + next(null, txp); + }); }); }, function(txp, next) { @@ -1257,22 +1184,20 @@ describe('Copay server', function() { function(next) { server.getPendingTxs({}, function(err, txps) { should.not.exist(err); - txps.length.should.equal(0); - next(); - }); - }, - function(next) { - server.getTx({ - id: txpId - }, function(err, txp) { - should.not.exist(err); - txp.isPending().should.be.false; - txp.isRejected().should.be.false; + txps.length.should.equal(1); + var txp = txps[0]; + txp.isPending().should.be.true; txp.isAccepted().should.be.true; - txp.isBroadcasted().should.be.true; - txp.txid.should.equal('999'); + txp.isBroadcasted().should.be.false; + should.not.exist(txp.txid); txp.actions.length.should.equal(2); - done(); + server.getNotifications({}, function(err, notifications) { + should.not.exist(err); + var last = _.last(notifications); + last.type.should.equal('TxProposalFinallyAccepted'); + last.data.txProposalId.should.equal(txp.id); + done(); + }); }); }, ]); @@ -1553,25 +1478,31 @@ describe('Copay server', function() { server.getPendingTxs({}, function(err, txs) { var tx = txs[2]; var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey); - helpers.stubBroadcast('1122334455'); sinon.spy(server, 'emit'); server.signTx({ txProposalId: tx.id, signatures: signatures, }, function(err) { - server.getNotifications({ - limit: 3, - reverse: true, - }, function(err, notifications) { + should.not.exist(err); + helpers.stubBroadcast('1122334455'); + server.broadcastTx({ + txProposalId: tx.id + }, function(err, txp) { should.not.exist(err); - var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['NewOutgoingTx', 'TxProposalFinallyAccepted', 'TxProposalAcceptedBy']); - // Check also events - server.emit.getCall(0).args[0].type.should.equal('TxProposalAcceptedBy'); - server.emit.getCall(1).args[0].type.should.equal('TxProposalFinallyAccepted');; - server.emit.getCall(2).args[0].type.should.equal('NewOutgoingTx'); + server.getNotifications({ + limit: 3, + reverse: true, + }, function(err, notifications) { + should.not.exist(err); + var types = _.pluck(notifications, 'type'); + types.should.deep.equal(['NewOutgoingTx', 'TxProposalFinallyAccepted', 'TxProposalAcceptedBy']); + // Check also events + server.emit.getCall(0).args[0].type.should.equal('TxProposalAcceptedBy'); + server.emit.getCall(1).args[0].type.should.equal('TxProposalFinallyAccepted');; + server.emit.getCall(2).args[0].type.should.equal('NewOutgoingTx'); - done(); + done(); + }); }); }); }); @@ -1860,44 +1791,50 @@ describe('Copay server', function() { should.not.exist(err); should.exist(tx); - helpers.stubBroadcast('1122334455'); var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey); server.signTx({ txProposalId: tx.id, signatures: signatures, }, function(err, tx) { should.not.exist(err); - var txs = [{ - txid: '1122334455', - confirmations: 1, - fees: 5460, - minedTs: 1, - inputs: [{ - address: tx.inputs[0].address, - amount: utxos[0].satoshis, - }], - outputs: [{ - address: 'external', - amount: helpers.toSatoshi(80) - 5460, - }, { - address: changeAddresses[0].address, - amount: helpers.toSatoshi(20) - 5460, - }], - }]; - helpers.stubHistory(txs); - - server.getTxHistory({}, function(err, txs) { + + helpers.stubBroadcast('1122334455'); + server.broadcastTx({ + txProposalId: tx.id + }, function(err, txp) { should.not.exist(err); - should.exist(txs); - txs.length.should.equal(1); - var tx = txs[0]; - tx.action.should.equal('sent'); - tx.amount.should.equal(helpers.toSatoshi(80)); - tx.message.should.equal('some message'); - tx.actions.length.should.equal(1); - tx.actions[0].type.should.equal('accept'); - tx.actions[0].copayerName.should.equal('copayer 1'); - done(); + var txs = [{ + txid: '1122334455', + confirmations: 1, + fees: 5460, + minedTs: 1, + inputs: [{ + address: tx.inputs[0].address, + amount: utxos[0].satoshis, + }], + outputs: [{ + address: 'external', + amount: helpers.toSatoshi(80) - 5460, + }, { + address: changeAddresses[0].address, + amount: helpers.toSatoshi(20) - 5460, + }], + }]; + helpers.stubHistory(txs); + + server.getTxHistory({}, function(err, txs) { + should.not.exist(err); + should.exist(txs); + txs.length.should.equal(1); + var tx = txs[0]; + tx.action.should.equal('sent'); + tx.amount.should.equal(helpers.toSatoshi(80)); + tx.message.should.equal('some message'); + tx.actions.length.should.equal(1); + tx.actions[0].type.should.equal('accept'); + tx.actions[0].copayerName.should.equal('copayer 1'); + done(); + }); }); }); });