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', {
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);
}
});
});
});
};
@ -756,7 +741,7 @@ WalletService.prototype.broadcastTx = function(opts, cb) {
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) {
should.not.exist(err);
txp.status.should.equal('broadcasted');
txp.status.should.equal('accepted');
done();
});
});
@ -1141,22 +1141,25 @@ 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);
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();
});
});
});
});
});
});
it('Send and broadcast in 2-3 wallet', function(done) {
helpers.createAndJoinWallet(clients, 2, 3, function(err, w) {
clients[0].createAddress(function(err, x0) {
@ -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,13 +1187,15 @@ 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);
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();
});
});
@ -1199,6 +1204,7 @@ describe('client API ', function() {
});
});
});
});
it('Send, reject, 2 signs and broadcast in 2-3 wallet', function(done) {
helpers.createAndJoinWallet(clients, 2, 3, function(err, w) {
@ -1211,20 +1217,22 @@ 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);
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();
});
});
@ -1233,6 +1241,7 @@ describe('client API ', function() {
});
});
});
});
it('Send, reject in 3-4 wallet', function(done) {
helpers.createAndJoinWallet(clients, 3, 4, function(err, w) {
@ -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();
});

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() {
@ -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,13 +1155,19 @@ 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');
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) {
helpers.getAuthServer(wallet.copayers[1].id, function(server, wallet) {
@ -1257,23 +1184,21 @@ 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);
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,12 +1478,17 @@ 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) {
should.not.exist(err);
helpers.stubBroadcast('1122334455');
server.broadcastTx({
txProposalId: tx.id
}, function(err, txp) {
should.not.exist(err);
server.getNotifications({
limit: 3,
reverse: true,
@ -1577,6 +1507,7 @@ describe('Copay server', function() {
});
});
});
});
describe('#removeWallet', function() {
var server, wallet, clock;
@ -1860,13 +1791,18 @@ 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);
helpers.stubBroadcast('1122334455');
server.broadcastTx({
txProposalId: tx.id
}, function(err, txp) {
should.not.exist(err);
var txs = [{
txid: '1122334455',
confirmations: 1,
@ -1904,4 +1840,5 @@ describe('Copay server', function() {
});
});
});
});
});

Loading…
Cancel
Save