Browse Source

Merge pull request #91 from isocolsky/sign_without_broadcast

Sign without broadcast
activeAddress
Matias Alejo Garcia 10 years ago
parent
commit
30000d010b
  1. 19
      lib/server.js
  2. 109
      test/integration/clientApi.js
  3. 129
      test/integration/server.js

19
lib/server.js

@ -695,26 +695,11 @@ WalletService.prototype.signTx = function(opts, cb) {
self._notify('TxProposalFinallyAccepted', { self._notify('TxProposalFinallyAccepted', {
txProposalId: opts.txProposalId, 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); return cb(null, txp);
}); });
}); });
} else {
return cb(null, txp);
}
});
});
}); });
}; };
@ -756,7 +741,7 @@ WalletService.prototype.broadcastTx = function(opts, cb) {
txid: txid txid: txid
}); });
return cb(null, txid); return cb(null, txp);
}); });
}); });
}); });

109
test/integration/clientApi.js

@ -722,7 +722,7 @@ describe('client API ', function() {
clients[0].signTxProposal(txp, function(err, txp) { clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err); should.not.exist(err);
txp.status.should.equal('broadcasted'); txp.status.should.equal('accepted');
done(); done();
}); });
}); });
@ -1141,22 +1141,25 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1', message: 'hello 1-1',
}; };
clients[0].sendTxProposal(opts, function(err, x) { clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err); should.not.exist(err);
x.requiredRejections.should.equal(1); txp.requiredRejections.should.equal(1);
x.requiredSignatures.should.equal(1); txp.requiredSignatures.should.equal(1);
x.status.should.equal('pending'); txp.status.should.equal('pending');
x.changeAddress.path.should.equal('m/2147483647/1/0'); txp.changeAddress.path.should.equal('m/2147483647/1/0');
clients[0].signTxProposal(x, function(err, tx) { clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err); should.not.exist(err);
tx.status.should.equal('broadcasted'); txp.status.should.equal('accepted');
tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); clients[0].broadcastTxProposal(txp, function(err, txp) {
txp.status.should.equal('broadcasted');
txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done(); done();
}); });
}); });
}); });
}); });
}); });
});
it('Send and broadcast in 2-3 wallet', function(done) { it('Send and broadcast in 2-3 wallet', function(done) {
helpers.createAndJoinWallet(clients, 2, 3, function(err, w) { helpers.createAndJoinWallet(clients, 2, 3, function(err, w) {
clients[0].createAddress(function(err, x0) { clients[0].createAddress(function(err, x0) {
@ -1168,14 +1171,14 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1', message: 'hello 1-1',
}; };
clients[0].sendTxProposal(opts, function(err, x) { clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err); should.not.exist(err);
clients[0].getStatus(function(err, st) { clients[0].getStatus(function(err, st) {
should.not.exist(err); should.not.exist(err);
var x = st.pendingTxps[0]; var txp = st.pendingTxps[0];
x.status.should.equal('pending'); txp.status.should.equal('pending');
x.requiredRejections.should.equal(2); txp.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(2); txp.requiredSignatures.should.equal(2);
var w = st.wallet; var w = st.wallet;
w.copayers.length.should.equal(3); w.copayers.length.should.equal(3);
w.status.should.equal('complete'); w.status.should.equal('complete');
@ -1184,13 +1187,15 @@ describe('client API ', function() {
b.lockedAmount.should.equal(1000000000); b.lockedAmount.should.equal(1000000000);
clients[0].signTxProposal(x, function(err, tx) { clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err, err); should.not.exist(err, err);
tx.status.should.equal('pending'); txp.status.should.equal('pending');
clients[1].signTxProposal(x, function(err, tx) { clients[1].signTxProposal(txp, function(err, txp) {
should.not.exist(err); should.not.exist(err);
tx.status.should.equal('broadcasted'); txp.status.should.equal('accepted');
tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); clients[1].broadcastTxProposal(txp, function(err, txp) {
txp.status.should.equal('broadcasted');
txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done(); done();
}); });
}); });
@ -1199,6 +1204,7 @@ describe('client API ', function() {
}); });
}); });
}); });
});
it('Send, reject, 2 signs and broadcast in 2-3 wallet', function(done) { it('Send, reject, 2 signs and broadcast in 2-3 wallet', function(done) {
helpers.createAndJoinWallet(clients, 2, 3, function(err, w) { helpers.createAndJoinWallet(clients, 2, 3, function(err, w) {
@ -1211,20 +1217,22 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1', message: 'hello 1-1',
}; };
clients[0].sendTxProposal(opts, function(err, x) { clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err); should.not.exist(err);
x.status.should.equal('pending'); txp.status.should.equal('pending');
x.requiredRejections.should.equal(2); txp.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(2); txp.requiredSignatures.should.equal(2);
clients[0].rejectTxProposal(x, 'wont sign', function(err, tx) { clients[0].rejectTxProposal(txp, 'wont sign', function(err, txp) {
should.not.exist(err, err); should.not.exist(err, err);
tx.status.should.equal('pending'); txp.status.should.equal('pending');
clients[1].signTxProposal(x, function(err, tx) { clients[1].signTxProposal(txp, function(err, txp) {
should.not.exist(err); should.not.exist(err);
clients[2].signTxProposal(x, function(err, tx) { clients[2].signTxProposal(txp, function(err, txp) {
should.not.exist(err); should.not.exist(err);
tx.status.should.equal('broadcasted'); txp.status.should.equal('accepted');
tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id); clients[2].broadcastTxProposal(txp, function(err, txp) {
txp.status.should.equal('broadcasted');
txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done(); done();
}); });
}); });
@ -1233,6 +1241,7 @@ describe('client API ', function() {
}); });
}); });
}); });
});
it('Send, reject in 3-4 wallet', function(done) { it('Send, reject in 3-4 wallet', function(done) {
helpers.createAndJoinWallet(clients, 3, 4, function(err, w) { helpers.createAndJoinWallet(clients, 3, 4, function(err, w) {
@ -1245,21 +1254,21 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1', message: 'hello 1-1',
}; };
clients[0].sendTxProposal(opts, function(err, x) { clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err); should.not.exist(err);
x.status.should.equal('pending'); txp.status.should.equal('pending');
x.requiredRejections.should.equal(2); txp.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(3); 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); should.not.exist(err, err);
tx.status.should.equal('pending'); txp.status.should.equal('pending');
clients[1].signTxProposal(x, function(err, tx) { clients[1].signTxProposal(txp, function(err, txp) {
should.not.exist(err); should.not.exist(err);
tx.status.should.equal('pending'); txp.status.should.equal('pending');
clients[2].rejectTxProposal(x, 'me neither', function(err, tx) { clients[2].rejectTxProposal(txp, 'me neither', function(err, txp) {
should.not.exist(err); should.not.exist(err);
tx.status.should.equal('rejected'); txp.status.should.equal('rejected');
done(); done();
}); });
}); });
@ -1280,19 +1289,21 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1', message: 'hello 1-1',
}; };
clients[0].sendTxProposal(opts, function(err, x) { clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err); should.not.exist(err);
x.status.should.equal('pending'); txp.status.should.equal('pending');
x.requiredRejections.should.equal(2); txp.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(2); txp.requiredSignatures.should.equal(2);
clients[0].signTxProposal(x, function(err, tx) { clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err, err); should.not.exist(err);
tx.status.should.equal('pending'); txp.status.should.equal('pending');
clients[0].signTxProposal(x, function(err, tx) { clients[0].signTxProposal(txp, function(err) {
should.exist(err);
err.code.should.contain('CVOTED'); 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); 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'); err.code.should.contain('CVOTED');
done(); done();
}); });

129
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() { 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; var txpId;
async.waterfall([ async.waterfall([
@ -1234,13 +1155,19 @@ describe('Copay server', function() {
txps.length.should.equal(1); txps.length.should.equal(1);
var txp = txps[0]; var txp = txps[0];
txp.isPending().should.be.true; txp.isPending().should.be.true;
txp.isRejected().should.be.false;
txp.isAccepted().should.be.false; txp.isAccepted().should.be.false;
txp.isRejected().should.be.false;
txp.isBroadcasted().should.be.false;
txp.actions.length.should.equal(1); txp.actions.length.should.equal(1);
var action = txp.getActionBy(wallet.copayers[0].id); var action = txp.getActionBy(wallet.copayers[0].id);
action.type.should.equal('accept'); action.type.should.equal('accept');
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
var last = _.last(notifications);
last.type.should.not.equal('TxProposalFinallyAccepted');
next(null, txp); next(null, txp);
}); });
});
}, },
function(txp, next) { function(txp, next) {
helpers.getAuthServer(wallet.copayers[1].id, function(server, wallet) { helpers.getAuthServer(wallet.copayers[1].id, function(server, wallet) {
@ -1257,23 +1184,21 @@ describe('Copay server', function() {
function(next) { function(next) {
server.getPendingTxs({}, function(err, txps) { server.getPendingTxs({}, function(err, txps) {
should.not.exist(err); should.not.exist(err);
txps.length.should.equal(0); txps.length.should.equal(1);
next(); var txp = txps[0];
}); txp.isPending().should.be.true;
},
function(next) {
server.getTx({
id: txpId
}, function(err, txp) {
should.not.exist(err);
txp.isPending().should.be.false;
txp.isRejected().should.be.false;
txp.isAccepted().should.be.true; txp.isAccepted().should.be.true;
txp.isBroadcasted().should.be.true; txp.isBroadcasted().should.be.false;
txp.txid.should.equal('999'); should.not.exist(txp.txid);
txp.actions.length.should.equal(2); txp.actions.length.should.equal(2);
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(); done();
}); });
});
}, },
]); ]);
}); });
@ -1553,12 +1478,17 @@ describe('Copay server', function() {
server.getPendingTxs({}, function(err, txs) { server.getPendingTxs({}, function(err, txs) {
var tx = txs[2]; var tx = txs[2];
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey); var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
helpers.stubBroadcast('1122334455');
sinon.spy(server, 'emit'); sinon.spy(server, 'emit');
server.signTx({ server.signTx({
txProposalId: tx.id, txProposalId: tx.id,
signatures: signatures, signatures: signatures,
}, function(err) { }, function(err) {
should.not.exist(err);
helpers.stubBroadcast('1122334455');
server.broadcastTx({
txProposalId: tx.id
}, function(err, txp) {
should.not.exist(err);
server.getNotifications({ server.getNotifications({
limit: 3, limit: 3,
reverse: true, reverse: true,
@ -1577,6 +1507,7 @@ describe('Copay server', function() {
}); });
}); });
}); });
});
describe('#removeWallet', function() { describe('#removeWallet', function() {
var server, wallet, clock; var server, wallet, clock;
@ -1860,13 +1791,18 @@ describe('Copay server', function() {
should.not.exist(err); should.not.exist(err);
should.exist(tx); should.exist(tx);
helpers.stubBroadcast('1122334455');
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey); var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
server.signTx({ server.signTx({
txProposalId: tx.id, txProposalId: tx.id,
signatures: signatures, signatures: signatures,
}, function(err, tx) { }, function(err, tx) {
should.not.exist(err); should.not.exist(err);
helpers.stubBroadcast('1122334455');
server.broadcastTx({
txProposalId: tx.id
}, function(err, txp) {
should.not.exist(err);
var txs = [{ var txs = [{
txid: '1122334455', txid: '1122334455',
confirmations: 1, confirmations: 1,
@ -1904,4 +1840,5 @@ describe('Copay server', function() {
}); });
}); });
}); });
});
}); });

Loading…
Cancel
Save